Skip to content

Commit

Permalink
Add default GitHub Actions to template
Browse files Browse the repository at this point in the history
  • Loading branch information
Layoric committed Nov 26, 2021
1 parent e31d49e commit 21292ea
Show file tree
Hide file tree
Showing 6 changed files with 283 additions and 2 deletions.
12 changes: 12 additions & 0 deletions .deploy/docker-compose-template.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
version: "3.9"
services:
${APP_NAME}:
image: ghcr.io/${IMAGE_REPO}:${RELEASE_VERSION}
restart: always
network_mode: bridge
ports:
- "80"
environment:
VIRTUAL_HOST: ${HOST_DOMAIN}
LETSENCRYPT_HOST: ${HOST_DOMAIN}
LETSENCRYPT_EMAIL: ${LETSENCRYPT_EMAIL}
40 changes: 40 additions & 0 deletions .deploy/nginx-proxy-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
version: '2'

services:
nginx-proxy:
image: jwilder/nginx-proxy
container_name: nginx-proxy
restart: always
ports:
- "80:80"
- "443:443"
volumes:
- conf:/etc/nginx/conf.d
- vhost:/etc/nginx/vhost.d
- html:/usr/share/nginx/html
- dhparam:/etc/nginx/dhparam
- certs:/etc/nginx/certs:ro
- /var/run/docker.sock:/tmp/docker.sock:ro
network_mode: bridge

letsencrypt:
image: jrcs/letsencrypt-nginx-proxy-companion:2.0
container_name: nginx-proxy-le
restart: always
environment:
- DEFAULT_EMAIL=you@example.com
volumes_from:
- nginx-proxy
volumes:
- certs:/etc/nginx/certs:rw
- acme:/etc/acme.sh
- /var/run/docker.sock:/var/run/docker.sock:ro
network_mode: bridge

volumes:
conf:
vhost:
html:
dhparam:
certs:
acme:
56 changes: 56 additions & 0 deletions .github/workflows/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# ServiceStack mix GitHub Actions
`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.

## Overview
`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.

GitHub Actions specified in `release.yml` then copy files remotely via scp and use `docker-compose` to run the app remotely via SSH.

## Deployment server setup
To get this working, a server needs to be setup with the following:

- SSH access
- docker
- docker-compose
- ports 443 and 80 for web access of your hosted application

This can be your own server or any cloud hosted server like Digital Ocean, AWS, Azure etc.

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.

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.

For example, once copied to remote `~/nginx-proxy-compose.yml`, the following command can be run on the remote server.

```
docker-compose -f ~/nginx-proxy-compose.yml up -d
```

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.

## GitHub Repository setup
The `release.yml` assumes 6 secrets have been setup.

- CR_PAT - GitHub Personal Token with read/write access to packages.
- DEPLOY_HOST - hostname used to SSH to, this can either be an IP address or subdomain with A record pointing to the server.
- DEPLOY_PORT - SSH port, usually `22`.
- DEPLOY_USERNAME - the username being logged into via SSH. Eg, `ubuntu`, `ec2-user`, `root` etc.
- DEPLOY_KEY - SSH private key used to remotely access deploy server/app host.
- LETSENCRYPT_EMAIL - Email address, required for Let's Encrypt automated TLS certificates.

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.

```bash
gh secret set CR_PAT -b"<CR_PAT, Container Registry Personal Access Token>"
gh secret set DEPLOY_HOST -b"<DEPLOY_HOST, domain or subdomain for your application and server host.>"
gh secret set DEPLOY_PORT -b"<DEPLOY_PORT, eg SSH port, usually 22>"
gh secret set DEPLOY_USERNAME -b"<DEPLOY_USERNAME, the username being logged into via SSH. Eg, `ubuntu`, `ec2-user`, `root` etc.>"
gh secret set DEPLOY_KEY -b"<DEPLOY_KEY, SSH private key used to remotely access deploy server/app host.>"
gh secret set LETSENCRYPT_EMAIL -b"<LETSENCRYPT_EMAIL, Email address for your TLS certificate generation, eg me@example.com>"
```

These secrets are used to populate variables within GitHub Actions and other configuration files.

## What's the process of `release.yml`?

![](https://raw.githubusercontent.com/ServiceStack/docs/master/docs/images/mix/release-ghr-vanilla-diagram.png)
35 changes: 35 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
name: Build

on:
pull_request: {}
push:
branches:
- '**' # matches every branch

jobs:
build:
runs-on: ubuntu-20.04
steps:
- name: checkout
uses: actions/checkout@v2.0.0

- name: setup .net core
uses: actions/setup-dotnet@v1.7.2
with:
dotnet-version: 6.0.100

- name: build
run: dotnet build
working-directory: .

- name: test
run: |
dotnet test
if [ $? -eq 0 ]; then
echo TESTS PASSED
else
echo TESTS FAILED
exit 1
fi
working-directory: ./MyApp.Tests

138 changes: 138 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
name: Release
on:
# Triggered on new GitHub Release
release:
types: [published]
# Triggered on every successful Build action
workflow_run:
workflows: ["Build"]
branches: [main,master]
types:
- completed
# Manual trigger for rollback to specific release or redeploy latest
workflow_dispatch:
inputs:
version:
default: latest
description: Tag you want to release.
required: true

jobs:
push_to_registry:
runs-on: ubuntu-20.04
if: ${{ github.event.workflow_run.conclusion != 'failure' }}
steps:
# Checkout latest or specific tag
- name: checkout
if: ${{ github.event.inputs.version == '' || github.event.inputs.version == 'latest' }}
uses: actions/checkout@v2
- name: checkout tag
if: ${{ github.event.inputs.version != '' && github.event.inputs.version != 'latest' }}
uses: actions/checkout@v2
with:
ref: refs/tags/${{ github.event.inputs.version }}

# Assign environment variables used in subsequent steps
- name: Env variable assignment
run: echo "image_repository_name=$(echo ${{ github.repository }} | tr '[:upper:]' '[:lower:]')" >> $GITHUB_ENV
# TAG_NAME defaults to 'latest' if not a release or manual deployment
- name: Assign version
run: |
echo "TAG_NAME=latest" >> $GITHUB_ENV
if [ "${{ github.event.release.tag_name }}" != "" ]; then
echo "TAG_NAME=${{ github.event.release.tag_name }}" >> $GITHUB_ENV
fi;
if [ "${{ github.event.inputs.version }}" != "" ]; then
echo "TAG_NAME=${{ github.event.inputs.version }}" >> $GITHUB_ENV
fi;
- name: Login to GitHub Container Registry
uses: docker/login-action@v1
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.CR_PAT }}

# Build and push new docker image, skip for manual redeploy other than 'latest'
- name: Build and push Docker images
uses: docker/build-push-action@v2.2.2
if: ${{ github.event.inputs.version == '' || github.event.inputs.version == 'latest' }}
with:
file: Dockerfile
context: .
push: true
tags: ghcr.io/${{ env.image_repository_name }}:${{ env.TAG_NAME }}

deploy_via_ssh:
needs: push_to_registry
runs-on: ubuntu-20.04
if: ${{ github.event.workflow_run.conclusion != 'failure' }}
steps:
# Checkout latest or specific tag
- name: checkout
if: ${{ github.event.inputs.version == '' || github.event.inputs.version == 'latest' }}
uses: actions/checkout@v2
- name: checkout tag
if: ${{ github.event.inputs.version != '' && github.event.inputs.version != 'latest' }}
uses: actions/checkout@v2
with:
ref: refs/tags/${{ github.event.inputs.version }}

- name: repository name fix and env
run: |
echo "image_repository_name=$(echo ${{ github.repository }} | tr '[:upper:]' '[:lower:]')" >> $GITHUB_ENV
echo "domain=${{ secrets.DEPLOY_HOST }}" >> $GITHUB_ENV
echo "letsencrypt_email=${{ secrets.LETSENCRYPT_EMAIL }}" >> $GITHUB_ENV
echo "TAG_NAME=latest" >> $GITHUB_ENV
if [ "${{ github.event.release.tag_name }}" != "" ]; then
echo "TAG_NAME=${{ github.event.release.tag_name }}" >> $GITHUB_ENV
fi;
if [ "${{ github.event.inputs.version }}" != "" ]; then
echo "TAG_NAME=${{ github.event.inputs.version }}" >> $GITHUB_ENV
fi;
# Populate docker-compose.yml with variables from build process, including TAG_NAME.
- name: docker-compose file prep
uses: danielr1996/envsubst-action@1.0.0
env:
RELEASE_VERSION: ${{ env.TAG_NAME }}
IMAGE_REPO: ${{ env.image_repository_name }}
APP_NAME: ${{ github.event.repository.name }}
HOST_DOMAIN: ${{ env.domain }}
LETSENCRYPT_EMAIL: ${{ env.letsencrypt_email }}
with:
input: .deploy/docker-compose-template.yml
output: .deploy/${{ github.event.repository.name }}-docker-compose.yml

# Copy only the docker-compose.yml to remote server home folder
- name: copy compose file via scp
uses: appleboy/scp-action@v0.1.1
with:
host: ${{ secrets.DEPLOY_HOST }}
username: ${{ secrets.DEPLOY_USERNAME }}
port: 22
key: ${{ secrets.DEPLOY_KEY }}
source: ".deploy/${{ github.event.repository.name }}-docker-compose.yml"
target: "~/"

- name: Set the value
run: |
echo "GH_TOKEN=${{ secrets.CR_PAT }}" >> $GITHUB_ENV
echo "USERNAME=${{ secrets.DEPLOY_USERNAME }}" >> $GITHUB_ENV
# Deploy Docker image with ServiceStack application using `docker compose up` remotely
- name: remote docker-compose up via ssh
uses: appleboy/ssh-action@v0.1.4
env:
APPTOKEN: ${{ env.GH_TOKEN }}
USERNAME: ${{ env.USERNAME }}
with:
host: ${{ secrets.DEPLOY_HOST }}
username: ${{ secrets.DEPLOY_USERNAME }}
key: ${{ secrets.DEPLOY_KEY }}
port: 22
envs: APPTOKEN,USERNAME
script: |
echo $APPTOKEN | docker login ghcr.io -u $USERNAME --password-stdin
docker-compose -f ~/.deploy/${{ github.event.repository.name }}-docker-compose.yml pull
docker-compose -f ~/.deploy/${{ github.event.repository.name }}-docker-compose.yml up -d
4 changes: 2 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build
FROM mcr.microsoft.com/dotnet/sdk:6.0-focal AS build
WORKDIR /app

COPY . .
Expand All @@ -7,7 +7,7 @@ RUN dotnet restore
WORKDIR /app/MyApp
RUN dotnet publish -c release -o /out --no-restore

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

0 comments on commit 21292ea

Please sign in to comment.