Skip to content

Commit c53243a

Browse files
committed
Upgrade to .NET 8 + convert to kamal
1 parent ae87b73 commit c53243a

15 files changed

+411
-113
lines changed

.github/workflows/build.yml

+5-5
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-latest
1212
steps:
1313
- name: checkout
14-
uses: actions/checkout@v2.0.0
14+
uses: actions/checkout@v3
1515

16-
- name: setup .net core
17-
uses: actions/setup-dotnet@v1.7.2
16+
- name: Setup dotnet
17+
uses: actions/setup-dotnet@v3
1818
with:
19-
dotnet-version: 6.0.100
19+
dotnet-version: '8.*'
2020

2121
- name: build
2222
run: dotnet build

.github/workflows/release.yml

+3-72
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ on:
2222

2323
jobs:
2424
push_to_registry:
25-
runs-on: ubuntu-22.04
25+
runs-on: ubuntu-latest
2626
if: ${{ github.event.workflow_run.conclusion != 'failure' }}
2727
steps:
2828
# Checkout latest or specific tag
@@ -50,87 +50,18 @@ jobs:
5050
fi;
5151
5252
- name: Login to GitHub Container Registry
53-
uses: docker/login-action@v2
53+
uses: docker/login-action@v3
5454
with:
5555
registry: ghcr.io
5656
username: ${{ github.actor }}
5757
password: ${{ secrets.GITHUB_TOKEN }}
5858

5959
# Build and push new docker image, skip for manual redeploy other than 'latest'
6060
- name: Build and push Docker images
61-
uses: docker/build-push-action@v3
61+
uses: docker/build-push-action@v6
6262
if: ${{ github.event.inputs.version == '' || github.event.inputs.version == 'latest' }}
6363
with:
6464
file: Dockerfile
6565
context: .
6666
push: true
6767
tags: ghcr.io/${{ env.image_repository_name }}:${{ env.TAG_NAME }}
68-
69-
deploy_via_ssh:
70-
needs: push_to_registry
71-
runs-on: ubuntu-22.04
72-
if: ${{ github.event.workflow_run.conclusion != 'failure' }}
73-
steps:
74-
# Checkout latest or specific tag
75-
- name: checkout
76-
if: ${{ github.event.inputs.version == '' || github.event.inputs.version == 'latest' }}
77-
uses: actions/checkout@v3
78-
- name: checkout tag
79-
if: ${{ github.event.inputs.version != '' && github.event.inputs.version != 'latest' }}
80-
uses: actions/checkout@v3
81-
with:
82-
ref: refs/tags/${{ github.event.inputs.version }}
83-
84-
- name: repository name fix and env
85-
run: |
86-
echo "image_repository_name=$(echo ${{ github.repository }} | tr '[:upper:]' '[:lower:]')" >> $GITHUB_ENV
87-
echo "domain=${{ secrets.DEPLOY_HOST }}" >> $GITHUB_ENV
88-
echo "letsencrypt_email=${{ secrets.LETSENCRYPT_EMAIL }}" >> $GITHUB_ENV
89-
echo "TAG_NAME=latest" >> $GITHUB_ENV
90-
if [ "${{ github.event.release.tag_name }}" != "" ]; then
91-
echo "TAG_NAME=${{ github.event.release.tag_name }}" >> $GITHUB_ENV
92-
fi;
93-
if [ "${{ github.event.inputs.version }}" != "" ]; then
94-
echo "TAG_NAME=${{ github.event.inputs.version }}" >> $GITHUB_ENV
95-
fi;
96-
97-
# Populate docker-compose.yml with variables from build process, including TAG_NAME.
98-
- name: docker-compose file prep
99-
uses: danielr1996/envsubst-action@1.0.0
100-
env:
101-
RELEASE_VERSION: ${{ env.TAG_NAME }}
102-
IMAGE_REPO: ${{ env.image_repository_name }}
103-
APP_NAME: ${{ github.event.repository.name }}
104-
HOST_DOMAIN: ${{ env.domain }}
105-
LETSENCRYPT_EMAIL: ${{ env.letsencrypt_email }}
106-
with:
107-
input: .deploy/docker-compose-template.yml
108-
output: .deploy/${{ github.event.repository.name }}-docker-compose.yml
109-
110-
# Copy only the docker-compose.yml to remote server home folder
111-
- name: copy compose file via scp
112-
uses: appleboy/scp-action@v0.1.3
113-
with:
114-
host: ${{ secrets.DEPLOY_HOST }}
115-
username: ${{ secrets.DEPLOY_USERNAME }}
116-
port: 22
117-
key: ${{ secrets.DEPLOY_KEY }}
118-
source: ".deploy/${{ github.event.repository.name }}-docker-compose.yml"
119-
target: "~/"
120-
121-
# Deploy Docker image with ServiceStack application using `docker compose up` remotely
122-
- name: remote docker-compose up via ssh
123-
uses: appleboy/ssh-action@v0.1.5
124-
env:
125-
APPTOKEN: ${{ secrets.GITHUB_TOKEN }}
126-
USERNAME: ${{ secrets.DEPLOY_USERNAME }}
127-
with:
128-
host: ${{ secrets.DEPLOY_HOST }}
129-
username: ${{ secrets.DEPLOY_USERNAME }}
130-
key: ${{ secrets.DEPLOY_KEY }}
131-
port: 22
132-
envs: APPTOKEN,USERNAME
133-
script: |
134-
echo $APPTOKEN | docker login ghcr.io -u $USERNAME --password-stdin
135-
docker-compose -f ~/.deploy/${{ github.event.repository.name }}-docker-compose.yml pull
136-
docker-compose -f ~/.deploy/${{ github.event.repository.name }}-docker-compose.yml up -d

.kamal/hooks/docker-setup.sample

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#!/bin/sh
2+
3+
echo "Docker set up on $KAMAL_HOSTS..."

.kamal/hooks/post-deploy.sample

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#!/bin/sh
2+
3+
# A sample post-deploy hook
4+
#
5+
# These environment variables are available:
6+
# KAMAL_RECORDED_AT
7+
# KAMAL_PERFORMER
8+
# KAMAL_VERSION
9+
# KAMAL_HOSTS
10+
# KAMAL_ROLE (if set)
11+
# KAMAL_DESTINATION (if set)
12+
# KAMAL_RUNTIME
13+
14+
echo "$KAMAL_PERFORMER deployed $KAMAL_VERSION to $KAMAL_DESTINATION in $KAMAL_RUNTIME seconds"

.kamal/hooks/post-proxy-reboot.sample

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#!/bin/sh
2+
3+
echo "Rebooted kamal-proxy on $KAMAL_HOSTS"

.kamal/hooks/pre-build.sample

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
#!/bin/sh
2+
3+
# A sample pre-build hook
4+
#
5+
# Checks:
6+
# 1. We have a clean checkout
7+
# 2. A remote is configured
8+
# 3. The branch has been pushed to the remote
9+
# 4. The version we are deploying matches the remote
10+
#
11+
# These environment variables are available:
12+
# KAMAL_RECORDED_AT
13+
# KAMAL_PERFORMER
14+
# KAMAL_VERSION
15+
# KAMAL_HOSTS
16+
# KAMAL_ROLE (if set)
17+
# KAMAL_DESTINATION (if set)
18+
19+
if [ -n "$(git status --porcelain)" ]; then
20+
echo "Git checkout is not clean, aborting..." >&2
21+
git status --porcelain >&2
22+
exit 1
23+
fi
24+
25+
first_remote=$(git remote)
26+
27+
if [ -z "$first_remote" ]; then
28+
echo "No git remote set, aborting..." >&2
29+
exit 1
30+
fi
31+
32+
current_branch=$(git branch --show-current)
33+
34+
if [ -z "$current_branch" ]; then
35+
echo "Not on a git branch, aborting..." >&2
36+
exit 1
37+
fi
38+
39+
remote_head=$(git ls-remote $first_remote --tags $current_branch | cut -f1)
40+
41+
if [ -z "$remote_head" ]; then
42+
echo "Branch not pushed to remote, aborting..." >&2
43+
exit 1
44+
fi
45+
46+
if [ "$KAMAL_VERSION" != "$remote_head" ]; then
47+
echo "Version ($KAMAL_VERSION) does not match remote HEAD ($remote_head), aborting..." >&2
48+
exit 1
49+
fi
50+
51+
exit 0

.kamal/hooks/pre-connect.sample

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
#!/usr/bin/env ruby
2+
3+
# A sample pre-connect check
4+
#
5+
# Warms DNS before connecting to hosts in parallel
6+
#
7+
# These environment variables are available:
8+
# KAMAL_RECORDED_AT
9+
# KAMAL_PERFORMER
10+
# KAMAL_VERSION
11+
# KAMAL_HOSTS
12+
# KAMAL_ROLE (if set)
13+
# KAMAL_DESTINATION (if set)
14+
# KAMAL_RUNTIME
15+
16+
hosts = ENV["KAMAL_HOSTS"].split(",")
17+
results = nil
18+
max = 3
19+
20+
elapsed = Benchmark.realtime do
21+
results = hosts.map do |host|
22+
Thread.new do
23+
tries = 1
24+
25+
begin
26+
Socket.getaddrinfo(host, 0, Socket::AF_UNSPEC, Socket::SOCK_STREAM, nil, Socket::AI_CANONNAME)
27+
rescue SocketError
28+
if tries < max
29+
puts "Retrying DNS warmup: #{host}"
30+
tries += 1
31+
sleep rand
32+
retry
33+
else
34+
puts "DNS warmup failed: #{host}"
35+
host
36+
end
37+
end
38+
39+
tries
40+
end
41+
end.map(&:value)
42+
end
43+
44+
retries = results.sum - hosts.size
45+
nopes = results.count { |r| r == max }
46+
47+
puts "Prewarmed %d DNS lookups in %.2f sec: %d retries, %d failures" % [ hosts.size, elapsed, retries, nopes ]

.kamal/hooks/pre-deploy.sample

+109
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
#!/usr/bin/env ruby
2+
3+
# A sample pre-deploy hook
4+
#
5+
# Checks the Github status of the build, waiting for a pending build to complete for up to 720 seconds.
6+
#
7+
# Fails unless the combined status is "success"
8+
#
9+
# These environment variables are available:
10+
# KAMAL_RECORDED_AT
11+
# KAMAL_PERFORMER
12+
# KAMAL_VERSION
13+
# KAMAL_HOSTS
14+
# KAMAL_COMMAND
15+
# KAMAL_SUBCOMMAND
16+
# KAMAL_ROLE (if set)
17+
# KAMAL_DESTINATION (if set)
18+
19+
# Only check the build status for production deployments
20+
if ENV["KAMAL_COMMAND"] == "rollback" || ENV["KAMAL_DESTINATION"] != "production"
21+
exit 0
22+
end
23+
24+
require "bundler/inline"
25+
26+
# true = install gems so this is fast on repeat invocations
27+
gemfile(true, quiet: true) do
28+
source "https://rubygems.org"
29+
30+
gem "octokit"
31+
gem "faraday-retry"
32+
end
33+
34+
MAX_ATTEMPTS = 72
35+
ATTEMPTS_GAP = 10
36+
37+
def exit_with_error(message)
38+
$stderr.puts message
39+
exit 1
40+
end
41+
42+
class GithubStatusChecks
43+
attr_reader :remote_url, :git_sha, :github_client, :combined_status
44+
45+
def initialize
46+
@remote_url = `git config --get remote.origin.url`.strip.delete_prefix("https://github.com/")
47+
@git_sha = `git rev-parse HEAD`.strip
48+
@github_client = Octokit::Client.new(access_token: ENV["GITHUB_TOKEN"])
49+
refresh!
50+
end
51+
52+
def refresh!
53+
@combined_status = github_client.combined_status(remote_url, git_sha)
54+
end
55+
56+
def state
57+
combined_status[:state]
58+
end
59+
60+
def first_status_url
61+
first_status = combined_status[:statuses].find { |status| status[:state] == state }
62+
first_status && first_status[:target_url]
63+
end
64+
65+
def complete_count
66+
combined_status[:statuses].count { |status| status[:state] != "pending"}
67+
end
68+
69+
def total_count
70+
combined_status[:statuses].count
71+
end
72+
73+
def current_status
74+
if total_count > 0
75+
"Completed #{complete_count}/#{total_count} checks, see #{first_status_url} ..."
76+
else
77+
"Build not started..."
78+
end
79+
end
80+
end
81+
82+
83+
$stdout.sync = true
84+
85+
puts "Checking build status..."
86+
attempts = 0
87+
checks = GithubStatusChecks.new
88+
89+
begin
90+
loop do
91+
case checks.state
92+
when "success"
93+
puts "Checks passed, see #{checks.first_status_url}"
94+
exit 0
95+
when "failure"
96+
exit_with_error "Checks failed, see #{checks.first_status_url}"
97+
when "pending"
98+
attempts += 1
99+
end
100+
101+
exit_with_error "Checks are still pending, gave up after #{MAX_ATTEMPTS * ATTEMPTS_GAP} seconds" if attempts == MAX_ATTEMPTS
102+
103+
puts checks.current_status
104+
sleep(ATTEMPTS_GAP)
105+
checks.refresh!
106+
end
107+
rescue Octokit::NotFound
108+
exit_with_error "Build status could not be found"
109+
end

.kamal/hooks/pre-proxy-reboot.sample

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#!/bin/sh
2+
3+
echo "Rebooting kamal-proxy on $KAMAL_HOSTS..."

.kamal/secrets

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Secrets defined here are available for reference under registry/password, env/secret, builder/secrets,
2+
# and accessories/*/env/secret in config/deploy.yml. All secrets should be pulled from either
3+
# password manager, ENV, or a file. DO NOT ENTER RAW CREDENTIALS HERE! This file needs to be safe for git.
4+
5+
# Option 1: Read secrets from the environment
6+
KAMAL_REGISTRY_PASSWORD=$KAMAL_REGISTRY_PASSWORD
7+
KAMAL_REGISTRY_USERNAME=$KAMAL_REGISTRY_USERNAME
8+
9+
# Option 2: Read secrets via a command
10+
# RAILS_MASTER_KEY=$(cat config/master.key)
11+
12+
# Option 3: Read secrets via kamal secrets helpers
13+
# These will handle logging in and fetching the secrets in as few calls as possible
14+
# There are adapters for 1Password, LastPass + Bitwarden
15+
#
16+
# SECRETS=$(kamal secrets fetch --adapter 1password --account my-account --from MyVault/MyItem KAMAL_REGISTRY_PASSWORD RAILS_MASTER_KEY)
17+
# KAMAL_REGISTRY_PASSWORD=$(kamal secrets extract KAMAL_REGISTRY_PASSWORD $SECRETS)
18+
# RAILS_MASTER_KEY=$(kamal secrets extract RAILS_MASTER_KEY $SECRETS)

Dockerfile

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
1+
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
22
WORKDIR /source
33

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

10-
FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS runtime
10+
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS runtime
11+
LABEL service="sharpscript"
12+
1113
WORKDIR /app
1214
COPY --from=build /app ./
1315
ENTRYPOINT ["dotnet", "SharpScript.dll"]

0 commit comments

Comments
 (0)