Skip to content

Commit 7056957

Browse files
committed
v1
1 parent e017741 commit 7056957

File tree

202 files changed

+6997
-3624
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

202 files changed

+6997
-3624
lines changed

.github/workflows/build-container.yml

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,6 @@ on:
1515
# Only update envs here if you need to change them for this workflow
1616
env:
1717
DOCKER_BUILDKIT: 1
18-
KAMAL_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }}
19-
KAMAL_REGISTRY_USERNAME: ${{ github.actor }}
2018
KAMAL_DEPLOY_HOST: ${{ secrets.KAMAL_DEPLOY_HOST }}
2119

2220
jobs:
@@ -34,6 +32,24 @@ jobs:
3432
echo "repository_name_lower=$(echo ${{ github.repository }} | cut -d '/' -f 2 | tr '[:upper:]' '[:lower:]')" >> $GITHUB_ENV
3533
echo "org_name=$(echo ${{ github.repository }} | cut -d '/' -f 1)" >> $GITHUB_ENV
3634
35+
# Set SERVICE_LABEL: derive from GITHUB_REPOSITORY (replace dots with dashes)
36+
echo "SERVICE_LABEL=$(echo ${{ github.repository }} | cut -d '/' -f 2 | tr '.' '-')" >> $GITHUB_ENV
37+
38+
# Set KAMAL_DEPLOY_HOST: use secret if available, otherwise use repository name
39+
if [ -n "${{ secrets.KAMAL_DEPLOY_HOST }}" ]; then
40+
DEPLOY_HOST="${{ secrets.KAMAL_DEPLOY_HOST }}"
41+
else
42+
DEPLOY_HOST="$(echo ${{ github.repository }} | cut -d '/' -f 2)"
43+
fi
44+
45+
# Validate KAMAL_DEPLOY_HOST contains at least one '.'
46+
if [[ ! "$DEPLOY_HOST" == *.* ]]; then
47+
echo "Error: KAMAL_DEPLOY_HOST must contain a hostname, e.g. example.com (got: $DEPLOY_HOST)"
48+
exit 1
49+
fi
50+
51+
echo "KAMAL_DEPLOY_HOST=$DEPLOY_HOST" >> $GITHUB_ENV
52+
3753
# This step is for the deployment of the templates only, safe to delete
3854
- name: Modify csproj for template deploy
3955
env:
@@ -62,6 +78,11 @@ jobs:
6278
working-directory: ./MyApp.Client
6379
run: npm install
6480

81+
- name: Build client
82+
if: steps.check_client.outputs.client_exists == 'true'
83+
working-directory: ./MyApp.Client
84+
run: npm run build
85+
6586
- name: Install x tool
6687
run: dotnet tool install -g x
6788

@@ -80,17 +101,24 @@ jobs:
80101
uses: docker/login-action@v3
81102
with:
82103
registry: ghcr.io
83-
username: ${{ env.KAMAL_REGISTRY_USERNAME }}
84-
password: ${{ env.KAMAL_REGISTRY_PASSWORD }}
104+
username: ${{ github.actor }}
105+
password: ${{ secrets.GITHUB_TOKEN }}
85106

86107
- name: Setup .NET
87108
uses: actions/setup-dotnet@v5
88109
with:
89-
dotnet-version: 8.0.x
110+
dotnet-version: 10.0.x
90111

91112
- name: Build and push Docker image
92113
env:
93114
SERVICESTACK_LICENSE: ${{ secrets.SERVICESTACK_LICENSE }}
94-
KAMAL_DEPLOY_HOST: ${{ secrets.KAMAL_DEPLOY_HOST }}
115+
KAMAL_DEPLOY_HOST: ${{ env.KAMAL_DEPLOY_HOST }}
116+
SERVICE_LABEL: ${{ env.SERVICE_LABEL }}
95117
run: |
96-
dotnet publish --os linux --arch x64 -c Release -p:ContainerRepository=${{ env.image_repository_name }} -p:ContainerRegistry=ghcr.io -p:ContainerImageTags=latest -p:ContainerPort=80 -p:ContainerEnvironmentVariable="SERVICESTACK_LICENSE=${{ env.SERVICESTACK_LICENSE }}"
118+
docker build \
119+
--build-arg SERVICESTACK_LICENSE="$SERVICESTACK_LICENSE" \
120+
--build-arg KAMAL_DEPLOY_HOST="$KAMAL_DEPLOY_HOST" \
121+
--build-arg SERVICE_LABEL="$SERVICE_LABEL" \
122+
-t ghcr.io/${{ env.image_repository_name }}:latest \
123+
-f Dockerfile .
124+
docker push ghcr.io/${{ env.image_repository_name }}:latest

.github/workflows/build.yml

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,26 @@ jobs:
1616
- name: Setup dotnet
1717
uses: actions/setup-dotnet@v5
1818
with:
19-
dotnet-version: 8.0.x
19+
dotnet-version: 10.0.x
20+
21+
- name: Restore NuGet packages (use repo NuGet.config)
22+
run: dotnet restore MyApp.slnx --configfile ./NuGet.Config
23+
24+
# If your feed requires authentication, enable and configure the step below.
25+
# This example uses a Personal Access Token stored in secrets.NUGET_API_KEY.
26+
# Alternatively, you can use the NuGet Authenticate action from Azure Pipelines.
27+
# - name: Authenticate private NuGet feed
28+
# env:
29+
# NUGET_API_KEY: ${{ secrets.NUGET_API_KEY }}
30+
# run: |
31+
# dotnet nuget add source "https://YOUR_FEED_URL/index.json" \
32+
# --name "PrivateFeed" \
33+
# --username "YOUR_USERNAME" \
34+
# --password "$NUGET_API_KEY" \
35+
# --store-password-in-clear-text
2036

2137
- name: build
22-
run: dotnet build
38+
run: dotnet build --no-restore
2339
working-directory: .
2440

2541
- name: test

.github/workflows/release.yml

Lines changed: 38 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,11 @@ on:
1414

1515
env:
1616
DOCKER_BUILDKIT: 1
17-
KAMAL_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }}
18-
KAMAL_REGISTRY_USERNAME: ${{ github.actor }}
1917
SERVICESTACK_LICENSE: ${{ secrets.SERVICESTACK_LICENSE }}
18+
KAMAL_DEPLOY_IP: ${{ secrets.KAMAL_DEPLOY_IP }}
19+
KAMAL_DEPLOY_HOST: ${{ secrets.KAMAL_DEPLOY_HOST }}
20+
KAMAL_REGISTRY_USERNAME: ${{ github.actor }}
21+
KAMAL_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }}
2022

2123
jobs:
2224
release:
@@ -38,24 +40,12 @@ jobs:
3840
echo "HAS_MIGRATIONS=false" >> $GITHUB_ENV
3941
fi
4042
41-
# This step is for the deployment of the templates only, safe to delete
42-
- name: Modify deploy.yml
43-
env:
44-
KAMAL_DEPLOY_IP: ${{ secrets.KAMAL_DEPLOY_IP }}
45-
if: env.KAMAL_DEPLOY_IP != null
46-
run: |
47-
sed -i "s/service: my-app/service: ${{ env.repository_name_lower }}/g" config/deploy.yml
48-
sed -i "s#image: my-user/myapp#image: ${{ env.image_repository_name }}#g" config/deploy.yml
49-
sed -i "s/- 192.168.0.1/- ${{ secrets.KAMAL_DEPLOY_IP }}/g" config/deploy.yml
50-
sed -i "s/host: my-app.example.com/host: ${{ secrets.KAMAL_DEPLOY_HOST }}/g" config/deploy.yml
51-
sed -i "s/MyApp/${{ env.repository_name }}/g" config/deploy.yml
52-
5343
- name: Login to GitHub Container Registry
5444
uses: docker/login-action@v3
5545
with:
5646
registry: ghcr.io
57-
username: ${{ env.KAMAL_REGISTRY_USERNAME }}
58-
password: ${{ env.KAMAL_REGISTRY_PASSWORD }}
47+
username: ${{ github.actor }}
48+
password: ${{ secrets.GITHUB_TOKEN }}
5949

6050
- name: Set up SSH key
6151
uses: webfactory/ssh-agent@v0.9.0
@@ -80,29 +70,47 @@ jobs:
8070
run: |
8171
kamal server bootstrap
8272
73+
- name: Ensure directories exist with correct permissions
74+
run: |
75+
echo "Creating directories with correct permissions"
76+
kamal server exec "mkdir -p /opt/docker/${{ env.repository_name }}/App_Data /opt/docker/${{ env.repository_name }}/initdb.d"
77+
78+
echo "Setting app file permissions"
79+
kamal server exec "chown -R 1654:1654 /opt/docker/${{ env.repository_name }}/App_Data /opt/docker/${{ env.repository_name }}/initdb.d"
80+
8381
- name: Check if first run and execute kamal app boot if necessary
8482
run: |
85-
FIRST_RUN_FILE=".${{ env.repository_name }}"
86-
if ! kamal server exec --no-interactive -q "test -f $FIRST_RUN_FILE"; then
87-
kamal server exec --no-interactive -q "touch $FIRST_RUN_FILE" || true
83+
FIRST_RUN_FILE="~/first-run/${{ env.repository_name }}"
84+
if ! kamal server exec -q "test -f $FIRST_RUN_FILE"; then
85+
kamal server exec -q "mkdir -p ~/first-run && touch $FIRST_RUN_FILE" || true
86+
87+
if [ -n "${{env.INIT_DB_SQL}}" ]; then
88+
echo "Initializing DB with INIT_DB_SQL secret..."
89+
# Save the SQL content to a temporary file
90+
echo "${{ env.INIT_DB_SQL }}" > init-db.sql
91+
cat init-db.sql | kamal server exec -i "cat > /opt/docker/${{ env.repository_name }}/initdb.d/${{ env.repository_name }}.sql" && rm init-db.sql || true
92+
fi
93+
# Start all kamal accessories
94+
kamal accessory boot all || true
95+
96+
# Deploy latest version
8897
kamal deploy -q -P --version latest || true
8998
else
9099
echo "Not first run, skipping kamal app boot"
91-
fi
100+
fi
92101
93-
- name: Ensure file permissions
102+
- name: Verify file permissions before deploy
103+
run: |
104+
kamal server exec --no-interactive "chown -R 1654:1654 /opt/docker/${{ env.repository_name }}/App_Data /opt/docker/${{ env.repository_name }}/initdb.d"
105+
106+
- name: Deploy with Kamal
94107
run: |
95-
kamal server exec --no-interactive "mkdir -p /opt/docker/${{ env.repository_name }}/App_Data && chown -R 1654:1654 /opt/docker/${{ env.repository_name }}"
108+
kamal lock release -v
109+
kamal server exec --no-interactive 'echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin'
110+
kamal server exec --no-interactive 'docker pull ghcr.io/${{ env.image_repository_name }}:latest'
111+
kamal deploy -P --version latest
96112
97113
- name: Migration
98114
if: env.HAS_MIGRATIONS == 'true'
99115
run: |
100-
kamal server exec --no-interactive 'echo "${{ env.KAMAL_REGISTRY_PASSWORD }}" | docker login ghcr.io -u ${{ env.KAMAL_REGISTRY_USERNAME }} --password-stdin'
101-
kamal server exec --no-interactive "docker pull ghcr.io/${{ env.image_repository_name }}:latest || true"
102116
kamal app exec --no-reuse --no-interactive --version=latest "--AppTasks=migrate"
103-
104-
- name: Deploy with Kamal
105-
run: |
106-
kamal lock release -v
107-
kamal server exec --no-interactive 'echo "${{ env.KAMAL_REGISTRY_PASSWORD }}" | docker login ghcr.io -u ${{ env.KAMAL_REGISTRY_USERNAME }} --password-stdin'
108-
kamal deploy -P --version latest

Dockerfile

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
# Multi-stage Dockerfile to run ASP.NET Core + Next.js in a single container
2+
3+
# Build arguments
4+
ARG KAMAL_DEPLOY_HOST
5+
ARG SERVICESTACK_LICENSE
6+
ARG SERVICE_LABEL
7+
8+
# 1. Build .NET app + Node.js apps
9+
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS dotnet-build
10+
ARG KAMAL_DEPLOY_HOST
11+
ENV KAMAL_DEPLOY_HOST=${KAMAL_DEPLOY_HOST}
12+
13+
WORKDIR /src
14+
15+
# Install Node.js for building Tailwind CSS and Next.js
16+
RUN apt-get update \
17+
&& apt-get install -y curl ca-certificates gnupg \
18+
&& curl -fsSL https://deb.nodesource.com/setup_24.x | bash - \
19+
&& apt-get install -y nodejs \
20+
&& apt-get clean \
21+
&& rm -rf /var/lib/apt/lists/*
22+
23+
# Copy solution and projects
24+
COPY MyApp.slnx ./
25+
COPY NuGet.Config ./
26+
COPY MyApp ./MyApp
27+
COPY MyApp.ServiceInterface ./MyApp.ServiceInterface
28+
COPY MyApp.ServiceModel ./MyApp.ServiceModel
29+
30+
# Build Tailwind CSS for .NET project
31+
WORKDIR /src/MyApp
32+
33+
# Download tailwindcss binary directly (avoiding sudo requirement in postinstall.js)
34+
RUN curl -sLO https://github.com/tailwindlabs/tailwindcss/releases/latest/download/tailwindcss-linux-x64 \
35+
&& chmod +x tailwindcss-linux-x64 \
36+
&& mv tailwindcss-linux-x64 /usr/local/bin/tailwindcss
37+
RUN npm run ui:build
38+
39+
# Build Next.js app
40+
WORKDIR /src/MyApp.Client
41+
COPY MyApp.Client/package*.json ./
42+
RUN npm ci
43+
COPY MyApp.Client/ ./
44+
RUN npm run build:prod
45+
46+
# Restore and publish .NET app
47+
WORKDIR /src
48+
RUN dotnet restore MyApp/MyApp.csproj
49+
# Disable .NET's built-in containerization (PublishProfile=DefaultContainer) inside Docker
50+
RUN dotnet publish MyApp/MyApp.csproj -c Release --no-restore -p:PublishProfile=
51+
52+
# 2. Runtime image with .NET + Node
53+
FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS final
54+
ARG SERVICESTACK_LICENSE
55+
ARG SERVICE_LABEL
56+
ARG KAMAL_DEPLOY_HOST
57+
58+
WORKDIR /app
59+
60+
# Label required by Kamal, must match config/deploy.yml service
61+
LABEL service="${SERVICE_LABEL}"
62+
63+
# Install Node.js >= 20.9 (Node 24.x LTS) and bash for the entrypoint script
64+
RUN apt-get update \
65+
&& apt-get install -y curl ca-certificates gnupg bash \
66+
&& curl -fsSL https://deb.nodesource.com/setup_24.x | bash - \
67+
&& apt-get install -y nodejs \
68+
&& apt-get clean \
69+
&& rm -rf /var/lib/apt/lists/*
70+
71+
# Copy published .NET app
72+
COPY --from=dotnet-build /src/MyApp/bin/Release/net10.0/publish ./api
73+
74+
# Copy built Next.js app (including dist, node_modules, public, etc.)
75+
COPY --from=dotnet-build /src/MyApp.Client ./client
76+
77+
ENV ASPNETCORE_URLS=http://0.0.0.0:8080 \
78+
INTERNAL_API_URL=http://127.0.0.1:8080 \
79+
NEXT_PORT=3000 \
80+
NODE_ENV=production \
81+
SERVICESTACK_LICENSE=$SERVICESTACK_LICENSE \
82+
KAMAL_DEPLOY_HOST=$KAMAL_DEPLOY_HOST
83+
84+
EXPOSE 8080
85+
86+
# Copy entrypoint script
87+
COPY entrypoint.sh /app/entrypoint.sh
88+
RUN chmod +x /app/entrypoint.sh
89+
90+
ENTRYPOINT ["/usr/bin/env", "bash", "/app/entrypoint.sh"]
91+

MyApp.Client/app/admin/page.tsx

Lines changed: 0 additions & 24 deletions
This file was deleted.

0 commit comments

Comments
 (0)