Skip to content

Commit 7d1cc02

Browse files
committed
Add default GitHub Actions to template
1 parent 06db474 commit 7d1cc02

File tree

6 files changed

+283
-2
lines changed

6 files changed

+283
-2
lines changed
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
version: "3.9"
2+
services:
3+
${APP_NAME}:
4+
image: ghcr.io/${IMAGE_REPO}:${RELEASE_VERSION}
5+
restart: always
6+
network_mode: bridge
7+
ports:
8+
- "80"
9+
environment:
10+
VIRTUAL_HOST: ${HOST_DOMAIN}
11+
LETSENCRYPT_HOST: ${HOST_DOMAIN}
12+
LETSENCRYPT_EMAIL: ${LETSENCRYPT_EMAIL}

.deploy/nginx-proxy-compose.yml

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
version: '2'
2+
3+
services:
4+
nginx-proxy:
5+
image: jwilder/nginx-proxy
6+
container_name: nginx-proxy
7+
restart: always
8+
ports:
9+
- "80:80"
10+
- "443:443"
11+
volumes:
12+
- conf:/etc/nginx/conf.d
13+
- vhost:/etc/nginx/vhost.d
14+
- html:/usr/share/nginx/html
15+
- dhparam:/etc/nginx/dhparam
16+
- certs:/etc/nginx/certs:ro
17+
- /var/run/docker.sock:/tmp/docker.sock:ro
18+
network_mode: bridge
19+
20+
letsencrypt:
21+
image: jrcs/letsencrypt-nginx-proxy-companion:2.0
22+
container_name: nginx-proxy-le
23+
restart: always
24+
environment:
25+
- DEFAULT_EMAIL=you@example.com
26+
volumes_from:
27+
- nginx-proxy
28+
volumes:
29+
- certs:/etc/nginx/certs:rw
30+
- acme:/etc/acme.sh
31+
- /var/run/docker.sock:/var/run/docker.sock:ro
32+
network_mode: bridge
33+
34+
volumes:
35+
conf:
36+
vhost:
37+
html:
38+
dhparam:
39+
certs:
40+
acme:

.github/workflows/README.md

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
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` assumes 6 secrets have been setup.
33+
34+
- CR_PAT - GitHub Personal Token with read/write access to packages.
35+
- DEPLOY_HOST - hostname used to SSH to, this can either be an IP address or subdomain with A record pointing to the server.
36+
- DEPLOY_PORT - SSH port, usually `22`.
37+
- DEPLOY_USERNAME - the username being logged into via SSH. Eg, `ubuntu`, `ec2-user`, `root` etc.
38+
- DEPLOY_KEY - SSH private key used to remotely access deploy server/app host.
39+
- LETSENCRYPT_EMAIL - Email address, required for Let's Encrypt automated TLS certificates.
40+
41+
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.
42+
43+
```bash
44+
gh secret set CR_PAT -b"<CR_PAT, Container Registry Personal Access Token>"
45+
gh secret set DEPLOY_HOST -b"<DEPLOY_HOST, domain or subdomain for your application and server host.>"
46+
gh secret set DEPLOY_PORT -b"<DEPLOY_PORT, eg SSH port, usually 22>"
47+
gh secret set DEPLOY_USERNAME -b"<DEPLOY_USERNAME, the username being logged into via SSH. Eg, `ubuntu`, `ec2-user`, `root` etc.>"
48+
gh secret set DEPLOY_KEY -b"<DEPLOY_KEY, SSH private key used to remotely access deploy server/app host.>"
49+
gh secret set LETSENCRYPT_EMAIL -b"<LETSENCRYPT_EMAIL, Email address for your TLS certificate generation, eg me@example.com>"
50+
```
51+
52+
These secrets are used to populate variables within GitHub Actions and other configuration files.
53+
54+
## What's the process of `release.yml`?
55+
56+
![](https://raw.githubusercontent.com/ServiceStack/docs/master/docs/images/mix/release-ghr-vanilla-diagram.png)

.github/workflows/build.yml

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
name: Build
2+
3+
on:
4+
pull_request: {}
5+
push:
6+
branches:
7+
- '**' # matches every branch
8+
9+
jobs:
10+
build:
11+
runs-on: ubuntu-20.04
12+
steps:
13+
- name: checkout
14+
uses: actions/checkout@v2.0.0
15+
16+
- name: setup .net core
17+
uses: actions/setup-dotnet@v1.7.2
18+
with:
19+
dotnet-version: 6.0.100
20+
21+
- name: build
22+
run: dotnet build
23+
working-directory: .
24+
25+
- name: test
26+
run: |
27+
dotnet test
28+
if [ $? -eq 0 ]; then
29+
echo TESTS PASSED
30+
else
31+
echo TESTS FAILED
32+
exit 1
33+
fi
34+
working-directory: ./MyApp.Tests
35+

.github/workflows/release.yml

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
name: Release
2+
on:
3+
# Triggered on new GitHub Release
4+
release:
5+
types: [published]
6+
# Triggered on every successful Build action
7+
workflow_run:
8+
workflows: ["Build"]
9+
branches: [main,master]
10+
types:
11+
- completed
12+
# Manual trigger for rollback to specific release or redeploy latest
13+
workflow_dispatch:
14+
inputs:
15+
version:
16+
default: latest
17+
description: Tag you want to release.
18+
required: true
19+
20+
jobs:
21+
push_to_registry:
22+
runs-on: ubuntu-20.04
23+
if: ${{ github.event.workflow_run.conclusion != 'failure' }}
24+
steps:
25+
# Checkout latest or specific tag
26+
- name: checkout
27+
if: ${{ github.event.inputs.version == '' || github.event.inputs.version == 'latest' }}
28+
uses: actions/checkout@v2
29+
- name: checkout tag
30+
if: ${{ github.event.inputs.version != '' && github.event.inputs.version != 'latest' }}
31+
uses: actions/checkout@v2
32+
with:
33+
ref: refs/tags/${{ github.event.inputs.version }}
34+
35+
# Assign environment variables used in subsequent steps
36+
- name: Env variable assignment
37+
run: echo "image_repository_name=$(echo ${{ github.repository }} | tr '[:upper:]' '[:lower:]')" >> $GITHUB_ENV
38+
# TAG_NAME defaults to 'latest' if not a release or manual deployment
39+
- name: Assign version
40+
run: |
41+
echo "TAG_NAME=latest" >> $GITHUB_ENV
42+
if [ "${{ github.event.release.tag_name }}" != "" ]; then
43+
echo "TAG_NAME=${{ github.event.release.tag_name }}" >> $GITHUB_ENV
44+
fi;
45+
if [ "${{ github.event.inputs.version }}" != "" ]; then
46+
echo "TAG_NAME=${{ github.event.inputs.version }}" >> $GITHUB_ENV
47+
fi;
48+
49+
- name: Login to GitHub Container Registry
50+
uses: docker/login-action@v1
51+
with:
52+
registry: ghcr.io
53+
username: ${{ github.repository_owner }}
54+
password: ${{ secrets.CR_PAT }}
55+
56+
# Build and push new docker image, skip for manual redeploy other than 'latest'
57+
- name: Build and push Docker images
58+
uses: docker/build-push-action@v2.2.2
59+
if: ${{ github.event.inputs.version == '' || github.event.inputs.version == 'latest' }}
60+
with:
61+
file: Dockerfile
62+
context: .
63+
push: true
64+
tags: ghcr.io/${{ env.image_repository_name }}:${{ env.TAG_NAME }}
65+
66+
deploy_via_ssh:
67+
needs: push_to_registry
68+
runs-on: ubuntu-20.04
69+
if: ${{ github.event.workflow_run.conclusion != 'failure' }}
70+
steps:
71+
# Checkout latest or specific tag
72+
- name: checkout
73+
if: ${{ github.event.inputs.version == '' || github.event.inputs.version == 'latest' }}
74+
uses: actions/checkout@v2
75+
- name: checkout tag
76+
if: ${{ github.event.inputs.version != '' && github.event.inputs.version != 'latest' }}
77+
uses: actions/checkout@v2
78+
with:
79+
ref: refs/tags/${{ github.event.inputs.version }}
80+
81+
- name: repository name fix and env
82+
run: |
83+
echo "image_repository_name=$(echo ${{ github.repository }} | tr '[:upper:]' '[:lower:]')" >> $GITHUB_ENV
84+
echo "domain=${{ secrets.DEPLOY_HOST }}" >> $GITHUB_ENV
85+
echo "letsencrypt_email=${{ secrets.LETSENCRYPT_EMAIL }}" >> $GITHUB_ENV
86+
echo "TAG_NAME=latest" >> $GITHUB_ENV
87+
if [ "${{ github.event.release.tag_name }}" != "" ]; then
88+
echo "TAG_NAME=${{ github.event.release.tag_name }}" >> $GITHUB_ENV
89+
fi;
90+
if [ "${{ github.event.inputs.version }}" != "" ]; then
91+
echo "TAG_NAME=${{ github.event.inputs.version }}" >> $GITHUB_ENV
92+
fi;
93+
94+
# Populate docker-compose.yml with variables from build process, including TAG_NAME.
95+
- name: docker-compose file prep
96+
uses: danielr1996/envsubst-action@1.0.0
97+
env:
98+
RELEASE_VERSION: ${{ env.TAG_NAME }}
99+
IMAGE_REPO: ${{ env.image_repository_name }}
100+
APP_NAME: ${{ github.event.repository.name }}
101+
HOST_DOMAIN: ${{ env.domain }}
102+
LETSENCRYPT_EMAIL: ${{ env.letsencrypt_email }}
103+
with:
104+
input: .deploy/docker-compose-template.yml
105+
output: .deploy/${{ github.event.repository.name }}-docker-compose.yml
106+
107+
# Copy only the docker-compose.yml to remote server home folder
108+
- name: copy compose file via scp
109+
uses: appleboy/scp-action@v0.1.1
110+
with:
111+
host: ${{ secrets.DEPLOY_HOST }}
112+
username: ${{ secrets.DEPLOY_USERNAME }}
113+
port: 22
114+
key: ${{ secrets.DEPLOY_KEY }}
115+
source: ".deploy/${{ github.event.repository.name }}-docker-compose.yml"
116+
target: "~/"
117+
118+
- name: Set the value
119+
run: |
120+
echo "GH_TOKEN=${{ secrets.CR_PAT }}" >> $GITHUB_ENV
121+
echo "USERNAME=${{ secrets.DEPLOY_USERNAME }}" >> $GITHUB_ENV
122+
123+
# Deploy Docker image with ServiceStack application using `docker compose up` remotely
124+
- name: remote docker-compose up via ssh
125+
uses: appleboy/ssh-action@v0.1.4
126+
env:
127+
APPTOKEN: ${{ env.GH_TOKEN }}
128+
USERNAME: ${{ env.USERNAME }}
129+
with:
130+
host: ${{ secrets.DEPLOY_HOST }}
131+
username: ${{ secrets.DEPLOY_USERNAME }}
132+
key: ${{ secrets.DEPLOY_KEY }}
133+
port: 22
134+
envs: APPTOKEN,USERNAME
135+
script: |
136+
echo $APPTOKEN | docker login ghcr.io -u $USERNAME --password-stdin
137+
docker-compose -f ~/.deploy/${{ github.event.repository.name }}-docker-compose.yml pull
138+
docker-compose -f ~/.deploy/${{ github.event.repository.name }}-docker-compose.yml up -d

Dockerfile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build
1+
FROM mcr.microsoft.com/dotnet/sdk:6.0-focal AS build
22
WORKDIR /app
33

44
COPY . .
@@ -7,7 +7,7 @@ RUN dotnet restore
77
WORKDIR /app/MyApp
88
RUN dotnet publish -c release -o /out --no-restore
99

10-
FROM mcr.microsoft.com/dotnet/aspnet:5.0 AS runtime
10+
FROM mcr.microsoft.com/dotnet/aspnet:6.0-focal AS runtime
1111
WORKDIR /app
1212
COPY --from=build /out ./
1313
ENTRYPOINT ["dotnet", "MyApp.dll"]

0 commit comments

Comments
 (0)