Skip to content

Commit d6cc4b4

Browse files
authored
Merge pull request #1 from NetCoreTemplates/sync-gh-actions
Update for sync-gh-actions
2 parents fdd1adf + 1a2d37a commit d6cc4b4

File tree

14 files changed

+208
-150
lines changed

14 files changed

+208
-150
lines changed

.deploy/docker-compose.yml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
version: "3.9"
2+
services:
3+
app:
4+
image: ghcr.io/${IMAGE_REPO}:${RELEASE_VERSION}
5+
restart: always
6+
ports:
7+
- "8080"
8+
container_name: ${APP_NAME}_app
9+
environment:
10+
VIRTUAL_HOST: ${HOST_DOMAIN}
11+
VIRTUAL_PORT: 8080 # New default ASP.NET port -> https://learn.microsoft.com/en-us/dotnet/core/compatibility/containers/8.0/aspnet-port
12+
LETSENCRYPT_HOST: ${HOST_DOMAIN}
13+
LETSENCRYPT_EMAIL: ${LETSENCRYPT_EMAIL}
14+
volumes:
15+
- app-mydb:/app/App_Data
16+
17+
networks:
18+
default:
19+
external: true
20+
name: nginx
21+
22+
volumes:
23+
app-mydb:

.deploy/nginx-proxy-compose.yml

100644100755
File mode changed.

.github/workflows/README.md

100644100755
Lines changed: 99 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,99 @@
1-
# ServiceStack mix GitHub Actions
2-
`release.yml` generated from `x mix release-ghr-vanilla`, this template in designed to help with CI deployment to a dedicated server with SSH access.
3-
4-
## Overview
5-
`release.yml` is designed to work with a ServiceStack app deploying directly to a single server via SSH. A docker image is built and stored on GitHub's `ghcr.io` docker registry when a GitHub Release is created.
6-
7-
GitHub Actions specified in `release.yml` then copy files remotely via scp and use `docker-compose` to run the app remotely via SSH.
8-
9-
## Deployment server setup
10-
To get this working, a server needs to be setup with the following:
11-
12-
- SSH access
13-
- docker
14-
- docker-compose
15-
- ports 443 and 80 for web access of your hosted application
16-
17-
This can be your own server or any cloud hosted server like Digital Ocean, AWS, Azure etc.
18-
19-
When setting up your server, you'll want to use a dedicated SSH key for access to be used by GitHub Actions. GitHub Actions will need the *private* SSH key within a GitHub Secret to authenticate. This can be done via ssh-keygen and copying the public key to the authorized clients on the server.
20-
21-
To let your server handle multiple ServiceStack applications and automate the generation and management of TLS certificates, an additional docker-compose file is provided via the `x mix` template, `nginx-proxy-compose.yml`. This docker-compose file is ready to run and can be copied to the deployment server.
22-
23-
For example, once copied to remote `~/nginx-proxy-compose.yml`, the following command can be run on the remote server.
24-
25-
```
26-
docker-compose -f ~/nginx-proxy-compose.yml up -d
27-
```
28-
29-
This will run an nginx reverse proxy along with a companion container that will watch for additional containers in the same docker network and attempt to initialize them with valid TLS certificates.
30-
31-
## GitHub Repository setup
32-
The `release.yml` uses the following secrets.
33-
34-
- DEPLOY_HOST - hostname used to SSH to, this can either be an IP address or subdomain with A record pointing to the server.
35-
- DEPLOY_USERNAME - the username being logged into via SSH. Eg, `ubuntu`, `ec2-user`, `root` etc.
36-
- DEPLOY_KEY - SSH private key used to remotely access deploy server/app host.
37-
- LETSENCRYPT_EMAIL - Email address, required for Let's Encrypt automated TLS certificates.
38-
39-
These secrets can use the [GitHub CLI](https://cli.github.com/manual/gh_secret_set) for ease of creation. Eg, using the GitHub CLI the following can be set.
40-
41-
```bash
42-
gh secret set DEPLOY_HOST -b"<DEPLOY_HOST, domain or subdomain for your application and server host.>"
43-
gh secret set DEPLOY_USERNAME -b"<DEPLOY_USERNAME, the username being logged into via SSH. Eg, `ubuntu`, `ec2-user`, `root` etc.>"
44-
gh secret set DEPLOY_KEY -b"<DEPLOY_KEY, SSH private key used to remotely access deploy server/app host.>"
45-
gh secret set LETSENCRYPT_EMAIL -b"<LETSENCRYPT_EMAIL, Email address for your TLS certificate generation, eg [email protected]>"
46-
```
47-
48-
These secrets are used to populate variables within GitHub Actions and other configuration files.
49-
50-
## What's the process of `release.yml`?
51-
52-
![](https://raw.githubusercontent.com/ServiceStack/docs/master/docs/images/mix/release-ghr-vanilla-diagram.png)
1+
## Overview
2+
3+
This template uses the deployment configurations for a ServiceStack .NET 8 application. The application is containerized using Docker and is set up to be automatically built and deployed via GitHub Actions. The recommended deployment target is a stand-alone Linux server running Ubuntu, with an NGINX reverse proxy also containerized using Docker, which a Docker Compose file is included in the template under the `.deploy` directory.
4+
5+
### Highlights
6+
- 🌐 **NGINX Reverse Proxy**: Utilizes an NGINX reverse proxy to handle web traffic and SSL termination.
7+
- 🚀 **GitHub Actions**: Leverages GitHub Actions for CI/CD, pushing Docker images to GitHub Container Registry and deploying them on a remote server.
8+
- 🐳 **Dockerized ServiceStack App**: The application is containerized, with the image built using `.NET 8`.
9+
- 🔄 **Automated Migrations**: Includes a separate service for running database migrations.
10+
11+
### Technology Stack
12+
- **Web Framework**: ServiceStack
13+
- **Language**: C# (.NET 8)
14+
- **Containerization**: Docker
15+
- **Reverse Proxy**: NGINX
16+
- **CI/CD**: GitHub Actions
17+
- **OS**: Ubuntu 22.04 (Deployment Server)
18+
19+
20+
21+
## Deployment Server Setup
22+
23+
To successfully host your ServiceStack applications, there are several components you need to set up on your deployment server. This guide assumes you're working on a standalone Linux server (Ubuntu is recommended) with SSH access enabled.
24+
25+
### Prerequisites
26+
27+
1. **SSH Access**: Required for GitHub Actions to communicate with your server.
28+
2. **Docker**: To containerize your application.
29+
3. **Docker-Compose**: For orchestrating multiple containers.
30+
4. **Ports**: 80 and 443 should be open for web access.
31+
5. **nginx-reverse-proxy**: For routing traffic to multiple ServiceStack applications and managing TLS certificates.
32+
33+
You can use any cloud-hosted or on-premises server like Digital Ocean, AWS, Azure, etc., for this setup.
34+
35+
### Step-by-Step Guide
36+
37+
#### 1. Install Docker and Docker-Compose
38+
39+
It is best to follow the [latest installation instructions on the Docker website](https://docs.docker.com/engine/install/ubuntu/) to ensure to have the correct setup with the latest patches.
40+
41+
#### 2. Configure SSH for GitHub Actions
42+
43+
Generate a dedicated SSH key pair to be used by GitHub Actions:
44+
45+
```bash
46+
ssh-keygen -t rsa -b 4096 -f ~/.ssh/github_actions
47+
```
48+
49+
Add the public key to the `authorized_keys` file on your server:
50+
51+
```bash
52+
cat ~/.ssh/github_actions.pub >> ~/.ssh/authorized_keys
53+
```
54+
55+
Then, add the *private* key to your GitHub Secrets as `DEPLOY_KEY` to enable GitHub Actions to SSH into the server securely.
56+
57+
#### 3. Set Up nginx-reverse-proxy
58+
59+
You should have a `docker-compose` file similar to the `nginx-proxy-compose.yml` in your repository. Upload this file to your server:
60+
61+
```bash
62+
scp nginx-proxy-compose.yml user@your_server:~/
63+
```
64+
65+
To bring up the nginx reverse proxy and its companion container for handling TLS certificates, run:
66+
67+
```bash
68+
docker compose -f ~/nginx-proxy-compose.yml up -d
69+
```
70+
71+
This will start an nginx reverse proxy along with a companion container. They will automatically watch for additional Docker containers on the same network and initialize them with valid TLS certificates.
72+
73+
74+
75+
## GitHub Repository Setup
76+
77+
Configuring your GitHub repository is an essential step for automating deployments via GitHub Actions. This guide assumes you have a `release.yml` workflow file in your repository's `.github/workflows/` directory, and your deployment server has been set up according to the [Deployment Server Setup](#Deployment-Server-Setup) guidelines.
78+
79+
### Secrets Configuration
80+
81+
Your GitHub Actions workflow requires the following secrets to be set in your GitHub repository:
82+
83+
1. **`DEPLOY_HOST`**: The hostname for SSH access. This can be either an IP address or a domain with an A-record pointing to your server.
84+
2. **`DEPLOY_USERNAME`**: The username for SSH login. Common examples include `ubuntu`, `ec2-user`, or `root`.
85+
3. **`DEPLOY_KEY`**: The SSH private key to securely access the deployment server. This should be the same key you've set up on your server for GitHub Actions.
86+
4. **`LETSENCRYPT_EMAIL`**: Your email address, required for Let's Encrypt automated TLS certificates.
87+
88+
#### Using GitHub CLI for Secret Management
89+
90+
You can conveniently set these secrets using the [GitHub CLI](https://cli.github.com/manual/gh_secret_set) like this:
91+
92+
```bash
93+
gh secret set DEPLOY_HOST --body="your-host-or-ip"
94+
gh secret set DEPLOY_USERNAME --body="your-username"
95+
gh secret set DEPLOY_KEY --bodyFile="path/to/your/ssh-private-key"
96+
gh secret set LETSENCRYPT_EMAIL --body="[email protected]"
97+
```
98+
99+
These secrets will populate environment variables within your GitHub Actions workflow and other configuration files, enabling secure and automated deployment of your ServiceStack applications.

.github/workflows/build.yml

100644100755
Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,15 @@ on:
88

99
jobs:
1010
build:
11-
runs-on: ubuntu-20.04
11+
runs-on: ubuntu-22.04
1212
steps:
1313
- name: checkout
14-
uses: actions/checkout@v2.0.0
14+
uses: actions/checkout@v3
1515

16-
- name: setup .net core
16+
- name: Setup dotnet
1717
uses: actions/setup-dotnet@v3
1818
with:
19-
dotnet-version: 6.0.100
19+
dotnet-version: '8.0'
2020

2121
- name: build
2222
run: dotnet build

.github/workflows/release.yml

100644100755
Lines changed: 61 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ jobs:
3434
uses: actions/checkout@v3
3535
with:
3636
ref: refs/tags/${{ github.event.inputs.version }}
37-
37+
3838
# Assign environment variables used in subsequent steps
3939
- name: Env variable assignment
4040
run: echo "image_repository_name=$(echo ${{ github.repository }} | tr '[:upper:]' '[:lower:]')" >> $GITHUB_ENV
@@ -48,33 +48,43 @@ jobs:
4848
if [ "${{ github.event.inputs.version }}" != "" ]; then
4949
echo "TAG_NAME=${{ github.event.inputs.version }}" >> $GITHUB_ENV
5050
fi;
51-
52-
- name: Setup node
53-
uses: actions/setup-node@v3
54-
with:
55-
node-version: 18
56-
# Run postinstall script
57-
- name: Build UI
58-
run: |
59-
npm run build
60-
working-directory: ./MyApp
61-
51+
if [ ! -z "${{ secrets.APPSETTINGS_PATCH }}" ]; then
52+
echo "HAS_APPSETTINGS_PATCH=true" >> $GITHUB_ENV
53+
else
54+
echo "HAS_APPSETTINGS_PATCH=false" >> $GITHUB_ENV
55+
fi;
56+
6257
- name: Login to GitHub Container Registry
6358
uses: docker/login-action@v2
6459
with:
6560
registry: ghcr.io
6661
username: ${{ github.actor }}
6762
password: ${{ secrets.GITHUB_TOKEN }}
63+
64+
65+
- name: Setup dotnet
66+
uses: actions/setup-dotnet@v3
67+
with:
68+
dotnet-version: '8.0'
69+
70+
- name: Install x tool
71+
if: env.HAS_APPSETTINGS_PATCH == 'true'
72+
run: dotnet tool install -g x
73+
74+
- name: Apply Production AppSettings
75+
if: env.HAS_APPSETTINGS_PATCH == 'true'
76+
working-directory: ./MyApp
77+
run: |
78+
cat <<EOF >> appsettings.json.patch
79+
${{ secrets.APPSETTINGS_PATCH }}
80+
EOF
81+
x patch appsettings.json.patch
82+
6883

6984
# Build and push new docker image, skip for manual redeploy other than 'latest'
70-
- name: Build and push Docker images
71-
uses: docker/build-push-action@v3
72-
if: ${{ github.event.inputs.version == '' || github.event.inputs.version == 'latest' }}
73-
with:
74-
file: Dockerfile
75-
context: .
76-
push: true
77-
tags: ghcr.io/${{ env.image_repository_name }}:${{ env.TAG_NAME }}
85+
- name: Build and push Docker image
86+
run: |
87+
dotnet publish --os linux --arch x64 -c Release -p:ContainerRepository=${{ env.image_repository_name }} -p:ContainerRegistry=ghcr.io -p:ContainerImageTags=${{ env.TAG_NAME }} -p:ContainerPort=80
7888
7989
deploy_via_ssh:
8090
needs: push_to_registry
@@ -105,14 +115,14 @@ jobs:
105115
- name: Create .env file
106116
run: |
107117
echo "Generating .env file"
108-
109-
echo "#Autogenerated .env file" > .env
110-
echo "HOST_DOMAIN=${{ secrets.DEPLOY_HOST }}" >> .env
111-
echo "LETSENCRYPT_EMAIL=${{ secrets.LETSENCRYPT_EMAIL }}" >> .env
112-
echo "APP_NAME=${{ github.event.repository.name }}" >> .env
113-
echo "IMAGE_REPO=${{ env.image_repository_name }}" >> .env
114-
echo "RELEASE_VERSION=${{ env.TAG_NAME }}" >> .env
115-
118+
119+
echo "# Autogenerated .env file" > .deploy/.env
120+
echo "HOST_DOMAIN=${{ secrets.DEPLOY_HOST }}" >> .deploy/.env
121+
echo "LETSENCRYPT_EMAIL=${{ secrets.LETSENCRYPT_EMAIL }}" >> .deploy/.env
122+
echo "APP_NAME=${{ github.event.repository.name }}" >> .deploy/.env
123+
echo "IMAGE_REPO=${{ env.image_repository_name }}" >> .deploy/.env
124+
echo "RELEASE_VERSION=${{ env.TAG_NAME }}" >> .deploy/.env
125+
116126
# Copy only the docker-compose.yml to remote server home folder
117127
- name: copy files to target server via scp
118128
uses: appleboy/[email protected]
@@ -121,8 +131,28 @@ jobs:
121131
username: ${{ secrets.DEPLOY_USERNAME }}
122132
port: 22
123133
key: ${{ secrets.DEPLOY_KEY }}
124-
source: "./docker-compose.yml,./docker-compose.prod.yml,./.env"
134+
strip_components: 2
135+
source: "./.deploy/docker-compose.yml,./.deploy/.env"
125136
target: "~/.deploy/${{ github.event.repository.name }}/"
137+
138+
- name: Setup App_Data volume directory
139+
uses: appleboy/[email protected]
140+
env:
141+
APPTOKEN: ${{ secrets.GITHUB_TOKEN }}
142+
USERNAME: ${{ secrets.DEPLOY_USERNAME }}
143+
with:
144+
host: ${{ secrets.DEPLOY_HOST }}
145+
username: ${{ secrets.DEPLOY_USERNAME }}
146+
key: ${{ secrets.DEPLOY_KEY }}
147+
port: 22
148+
envs: APPTOKEN,USERNAME
149+
script: |
150+
set -e
151+
echo $APPTOKEN | docker login ghcr.io -u $USERNAME --password-stdin
152+
cd ~/.deploy/${{ github.event.repository.name }}
153+
docker compose pull
154+
export APP_ID=$(docker compose run --entrypoint "id -u" --rm app)
155+
docker compose run --entrypoint "chown $APP_ID:$APP_ID /app/App_Data" --user root --rm app
126156
127157
# Deploy Docker image with your application using `docker compose up` remotely
128158
- name: remote docker-compose up via ssh
@@ -139,5 +169,5 @@ jobs:
139169
script: |
140170
echo $APPTOKEN | docker login ghcr.io -u $USERNAME --password-stdin
141171
cd ~/.deploy/${{ github.event.repository.name }}
142-
docker compose -f ./docker-compose.yml -f ./docker-compose.prod.yml pull
143-
docker compose -f ./docker-compose.yml -f ./docker-compose.prod.yml up app -d
172+
docker compose pull
173+
docker compose up app -d

Dockerfile

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

MyApp.ServiceInterface/MyApp.ServiceInterface.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<TargetFramework>net6.0</TargetFramework>
4+
<TargetFramework>net8.0</TargetFramework>
55
</PropertyGroup>
66

77
<ItemGroup>
8-
<PackageReference Include="ServiceStack" Version="6.*" />
8+
<PackageReference Include="ServiceStack" Version="8.*" />
99
</ItemGroup>
1010

1111
<ItemGroup>

MyApp.ServiceModel/MyApp.ServiceModel.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<TargetFramework>net6.0</TargetFramework>
4+
<TargetFramework>net8.0</TargetFramework>
55
</PropertyGroup>
66

77
<ItemGroup>
8-
<PackageReference Include="ServiceStack.Interfaces" Version="6.*" />
8+
<PackageReference Include="ServiceStack.Interfaces" Version="8.*" />
99
</ItemGroup>
1010

1111
<ItemGroup>

MyApp.Tests/MyApp.Tests.csproj

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<TargetFramework>net6.0</TargetFramework>
4+
<TargetFramework>net8.0</TargetFramework>
55
<DebugType>portable</DebugType>
66
<OutputType>Library</OutputType>
77
</PropertyGroup>
@@ -13,8 +13,8 @@
1313
<PackageReference Include="NUnit" Version="3.13.*" />
1414
<PackageReference Include="NUnit3TestAdapter" Version="4.1.*" />
1515
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.*" />
16-
<PackageReference Include="ServiceStack" Version="6.*" />
17-
<PackageReference Include="ServiceStack.Kestrel" Version="6.*" />
16+
<PackageReference Include="ServiceStack" Version="8.*" />
17+
<PackageReference Include="ServiceStack.Kestrel" Version="8.*" />
1818
</ItemGroup>
1919

2020
</Project>

0 commit comments

Comments
 (0)