Skip to content

Commit

Permalink
Kamal
Browse files Browse the repository at this point in the history
  • Loading branch information
stefanoverna committed Nov 23, 2024
1 parent 8ad4c6f commit 2d78027
Show file tree
Hide file tree
Showing 16 changed files with 429 additions and 11 deletions.
8 changes: 8 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
.astro
.DS_Store
.github
.kamal
config
node_modules
dist
.env
47 changes: 47 additions & 0 deletions .env.enc
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBmMVZEdlN1Y0JQb2t3MDBt
WkcvaVpNbVhQUVRSUnRiRldoZTRLbXpzK2tRCmtsc1A1MTd2bUZrWXNYUE1FallN
QXdoYlNuVDNlNFlPSjFtV1h6cnJyN1kKLT4gWDI1NTE5IG16MG1FNEE2Q2VQSDdC
YW1jcWxyQk4wTGkzc1E3b3V4Y0pKcGVrQUc4U00KWTRsd2JlK2E1M0toZWxpQnFB
dlBuNmtpSjltVW05SkdPSjk5blVyUzlSTQotPiBYMjU1MTkgR1piZGVTNCs2Q0Rr
bFpaZGNHNVNBV1M4eWZqOHBYZ0Q0OEZlK1l0ZFV3UQpZajc0MHBwTFFpb3NpV2RT
MGxXNGF5eU1EOWExUG9YaTFXSzlxNWUzcTZVCi0+IFgyNTUxOSBTWXc3QjZOR2ZO
SHNFQk1SVnQvNDVSWUp1QTZYQzJZdUx4dTkxSkE3Yld3CmowbTVYQjgvL2RjalFt
VWJsSnRFSWF1Tm8ySlNNNytZTVpBcGg5R3NibVUKLT4gWDI1NTE5IEV6dngzY3Qx
cmh6WkJWK0FJMXlNTlNtckRtdzNpQzNJUU8wUTRrdm9oZ0EKOXNwQjZMZkNEWVdi
a25BVTFjNGY4S3BxU0J1TU5KSVVxRUZIU1JBRlByOAotPiBYMjU1MTkgbzh5QU9s
RTFla0ZQemVmS3R3eDlkU3dIdG91cElVcHVqYjRkNGxUdnMyYwpBaS93enlsbE0w
M200bWRnejA2aDBmSjIvY0lhTEJScWRkZFRFNU5ndHM4Ci0tLSA1OXlQVHlQeWw1
NkpoSDlsYUZCM3pFeitWMjZRTWZIbFBKU09vN0dkUXRvCrMVvgfQQ3t38+jnMG1Q
oth7cjuDokDjQdxJ+jd30tBzDVxn73/xxV1KtEo7WouQPvG29Zj/m81m3lkoZyFm
m2PF+1MfAyc5yyh3j6Sjq07032rqoNtMQtEpmUT8k6K/YF/Q0WRzy0o9Te6b2RqG
r/EPdslRI3a2pmUiIB/euj6x7+fK2MWvpO+vrfCYUC9pU5DBl3ZGk8PUOoCN5hNL
akRiOTlebGLmiJucm0SHLMD1EvdxZdhBsHLML556+UMstTKcTBeETkhfmb2EFjey
5jEEOvUdI16giVz7AG60D4VQ2fhtkN9rCD9zN75zMX5lsEVDleo9pk69JqobeLsv
z0vvLJSDxKy0a1POF++2s12lh9SE8sWbDL+Q+nfse/48WOmoAOtXoWZeYbq5KkkW
BmEvvX9Lb0+oAheFgsge3QpHDRD7ghM40SnCGk6L6bkyMUHRp3ak+9Oxn9644NDG
tQX5iP0t+Nz4gFCUTBphn1HnpptjCBLv9pVPt4uHLOt4M3fmocC22SahKq2L+9dy
TNpdpSQE5K26E6SudhRH9U5Yzjcd5p4nBaBUGjtjvFP8PlBnti3w+HCncQKgGSHW
PgrcbE7YyOm9ODYJ6ILyj89K0l2+8eYjpDNlIj2WEnsGXCVZkj/yRsyvz4H2lzfX
QdEXkmq6DdjRKX0oijjtuffiHmAJkcuKI6VNi3H3kgu8WOtIGjmwWdY8srlx2hAg
AA4N1i74ngUH2bbr2RcvIvuyMiFTfpuS/1qo0wYGdKu42XhiZ1ovualSsQTTdRLT
CyyCyhYJSppmzU6++1IjqtYc6wbJAYlJ/NGR0yUDxE/rY+SvIhcMC/FOUHFt3CLn
t+LV7uaB9ZU+0q5FPkasPztnDooWqaUXfQhng/JowQhqM+LTdivDSjWWIHuz5UBD
cEvk9TC2fjQkktxBew93JPc/5P9gQq9N0EWBDbh9ExUahhExs9+zFZEt/UdWb77A
ZCZLYYVHEZsEVe/tX0BopS5kQB5uajQenexDuinhb7GSZ5fBmwkB46WSIjDT1fdC
eFxb9lClit9+f8Di5JIXSoGNq2faA1tn8Lee5TfcJyEHP7r9uRBlDDB/BZoiZvuK
ryZMhLBMhTdIl3Pmh9KsVMGODib0ekH9WxiS+62bdZnVodRKI9XCZKMQRveRERNF
qC1u9BK31cTgvt32zu+dDrPpq8Sw82N0x9BU1hLBAVfL4IPrmk7JfQdty+kqnLo0
04wnlh/inaz295I7gMZ3fWJJf9ZjjxhRhxM3/EbZNFj8P2PLOXpk4Ot1qMyzdVok
U4mmVY7fTV6Gp+iPRdhXTj0jL5MkS49TUmt+tuuiz7SgKwUA7DXcRxULd6I6VsPh
meI2EBl/uXAMu5SFz72LTrKvaHB0YW2THtjVievHTgcf8mkAzuKowtegNucLj3rP
WC5lWWxyreG3FnpjmsOJL7545DVyH578X8bzx5DuiNh/Q5w2g0a8bcj2WNIRfrCv
6hy5LHsIcim+3HT++Xoq8qKpiPzCJ/857Ak/Ic5BL1xWySGSPC2R6fKDKNboyXk1
lJHcawD1coNeazZ9Gwxgd4y8SzfzGICubS93rDFfMpIyrXGWdqjjhsX9R74fvZlN
vOvAv+a4S88HcLkTmuMJvrD0tFRi9hiBe0AqJVuk0KWRxk0osxvNvikMIHM6jL2P
goK3TkrobABhkLHN0CARfeSkAEB9gy6yL5Emn2N13wcSGWefl5mFk8skHFaqkaY8
nmdKkoD6NmUDKAEWAjd73Ggc0KNkkzFLDCEc4SIGJ7f7CpBDEqWWC3f3GAfOLzJ5
TsJkR84uXN1TfrXBnUn09ZHi+rNiHZdCBhmZ4Ud+p/aSjYA4FMV4KSHLH9VxaagI
zNQde2wEi3uyJCPEkxBV9pjKcatZpK6aA/fD+l7D7a+kUpYq/KQl42x+bh0PqMyg
FMCkW+ZAvRbPa4r/n1J0rnCxigMg0q4rQ7qmqUsmt3iJCqrKtm9rOKnGClwBOkS7
-----END AGE ENCRYPTED FILE-----
3 changes: 3 additions & 0 deletions .kamal/hooks/docker-setup.sample
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/bin/sh

echo "Docker set up on $KAMAL_HOSTS..."
14 changes: 14 additions & 0 deletions .kamal/hooks/post-deploy.sample
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#!/bin/sh

# A sample post-deploy hook
#
# These environment variables are available:
# KAMAL_RECORDED_AT
# KAMAL_PERFORMER
# KAMAL_VERSION
# KAMAL_HOSTS
# KAMAL_ROLE (if set)
# KAMAL_DESTINATION (if set)
# KAMAL_RUNTIME

echo "$KAMAL_PERFORMER deployed $KAMAL_VERSION to $KAMAL_DESTINATION in $KAMAL_RUNTIME seconds"
3 changes: 3 additions & 0 deletions .kamal/hooks/post-proxy-reboot.sample
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/bin/sh

echo "Rebooted kamal-proxy on $KAMAL_HOSTS"
51 changes: 51 additions & 0 deletions .kamal/hooks/pre-build.sample
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#!/bin/sh

# A sample pre-build hook
#
# Checks:
# 1. We have a clean checkout
# 2. A remote is configured
# 3. The branch has been pushed to the remote
# 4. The version we are deploying matches the remote
#
# These environment variables are available:
# KAMAL_RECORDED_AT
# KAMAL_PERFORMER
# KAMAL_VERSION
# KAMAL_HOSTS
# KAMAL_ROLE (if set)
# KAMAL_DESTINATION (if set)

if [ -n "$(git status --porcelain)" ]; then
echo "Git checkout is not clean, aborting..." >&2
git status --porcelain >&2
exit 1
fi

first_remote=$(git remote)

if [ -z "$first_remote" ]; then
echo "No git remote set, aborting..." >&2
exit 1
fi

current_branch=$(git branch --show-current)

if [ -z "$current_branch" ]; then
echo "Not on a git branch, aborting..." >&2
exit 1
fi

remote_head=$(git ls-remote $first_remote --tags $current_branch | cut -f1)

if [ -z "$remote_head" ]; then
echo "Branch not pushed to remote, aborting..." >&2
exit 1
fi

if [ "$KAMAL_VERSION" != "$remote_head" ]; then
echo "Version ($KAMAL_VERSION) does not match remote HEAD ($remote_head), aborting..." >&2
exit 1
fi

exit 0
47 changes: 47 additions & 0 deletions .kamal/hooks/pre-connect.sample
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#!/usr/bin/env ruby

# A sample pre-connect check
#
# Warms DNS before connecting to hosts in parallel
#
# These environment variables are available:
# KAMAL_RECORDED_AT
# KAMAL_PERFORMER
# KAMAL_VERSION
# KAMAL_HOSTS
# KAMAL_ROLE (if set)
# KAMAL_DESTINATION (if set)
# KAMAL_RUNTIME

hosts = ENV["KAMAL_HOSTS"].split(",")
results = nil
max = 3

elapsed = Benchmark.realtime do
results = hosts.map do |host|
Thread.new do
tries = 1

begin
Socket.getaddrinfo(host, 0, Socket::AF_UNSPEC, Socket::SOCK_STREAM, nil, Socket::AI_CANONNAME)
rescue SocketError
if tries < max
puts "Retrying DNS warmup: #{host}"
tries += 1
sleep rand
retry
else
puts "DNS warmup failed: #{host}"
host
end
end

tries
end
end.map(&:value)
end

retries = results.sum - hosts.size
nopes = results.count { |r| r == max }

puts "Prewarmed %d DNS lookups in %.2f sec: %d retries, %d failures" % [ hosts.size, elapsed, retries, nopes ]
109 changes: 109 additions & 0 deletions .kamal/hooks/pre-deploy.sample
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
#!/usr/bin/env ruby

# A sample pre-deploy hook
#
# Checks the Github status of the build, waiting for a pending build to complete for up to 720 seconds.
#
# Fails unless the combined status is "success"
#
# These environment variables are available:
# KAMAL_RECORDED_AT
# KAMAL_PERFORMER
# KAMAL_VERSION
# KAMAL_HOSTS
# KAMAL_COMMAND
# KAMAL_SUBCOMMAND
# KAMAL_ROLE (if set)
# KAMAL_DESTINATION (if set)

# Only check the build status for production deployments
if ENV["KAMAL_COMMAND"] == "rollback" || ENV["KAMAL_DESTINATION"] != "production"
exit 0
end

require "bundler/inline"

# true = install gems so this is fast on repeat invocations
gemfile(true, quiet: true) do
source "https://rubygems.org"

gem "octokit"
gem "faraday-retry"
end

MAX_ATTEMPTS = 72
ATTEMPTS_GAP = 10

def exit_with_error(message)
$stderr.puts message
exit 1
end

class GithubStatusChecks
attr_reader :remote_url, :git_sha, :github_client, :combined_status

def initialize
@remote_url = `git config --get remote.origin.url`.strip.delete_prefix("https://github.com/")
@git_sha = `git rev-parse HEAD`.strip
@github_client = Octokit::Client.new(access_token: ENV["GITHUB_TOKEN"])
refresh!
end

def refresh!
@combined_status = github_client.combined_status(remote_url, git_sha)
end

def state
combined_status[:state]
end

def first_status_url
first_status = combined_status[:statuses].find { |status| status[:state] == state }
first_status && first_status[:target_url]
end

def complete_count
combined_status[:statuses].count { |status| status[:state] != "pending"}
end

def total_count
combined_status[:statuses].count
end

def current_status
if total_count > 0
"Completed #{complete_count}/#{total_count} checks, see #{first_status_url} ..."
else
"Build not started..."
end
end
end


$stdout.sync = true

puts "Checking build status..."
attempts = 0
checks = GithubStatusChecks.new

begin
loop do
case checks.state
when "success"
puts "Checks passed, see #{checks.first_status_url}"
exit 0
when "failure"
exit_with_error "Checks failed, see #{checks.first_status_url}"
when "pending"
attempts += 1
end

exit_with_error "Checks are still pending, gave up after #{MAX_ATTEMPTS * ATTEMPTS_GAP} seconds" if attempts == MAX_ATTEMPTS

puts checks.current_status
sleep(ATTEMPTS_GAP)
checks.refresh!
end
rescue Octokit::NotFound
exit_with_error "Build status could not be found"
end
3 changes: 3 additions & 0 deletions .kamal/hooks/pre-proxy-reboot.sample
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/bin/sh

echo "Rebooting kamal-proxy on $KAMAL_HOSTS..."
15 changes: 15 additions & 0 deletions .kamal/secrets
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
KAVO_SECRETS=$(npx kavo files:decrypt .env -o | base64)

DATOCMS_API_TOKEN=$(echo $KAVO_SECRETS | base64 --decode | sed -n 's/^DATOCMS_API_TOKEN=//p')
FASTLY_KEY=$(echo $KAVO_SECRETS | base64 --decode | sed -n 's/^FASTLY_KEY=//p')
FASTLY_SERVICE_ID=$(echo $KAVO_SECRETS | base64 --decode | sed -n 's/^FASTLY_SERVICE_ID=//p')
MAILERLITE_TOKEN=$(echo $KAVO_SECRETS | base64 --decode | sed -n 's/^MAILERLITE_TOKEN=//p')
PIPEDRIVE_TOKEN=$(echo $KAVO_SECRETS | base64 --decode | sed -n 's/^PIPEDRIVE_TOKEN=//p')
RECAPTCHA_KEY=$(echo $KAVO_SECRETS | base64 --decode | sed -n 's/^RECAPTCHA_KEY=//p')
RECAPTCHA_SECRET_KEY=$(echo $KAVO_SECRETS | base64 --decode | sed -n 's/^RECAPTCHA_SECRET_KEY=//p')
ROLLBAR_TOKEN=$(echo $KAVO_SECRETS | base64 --decode | sed -n 's/^ROLLBAR_TOKEN=//p')
SECRET_API_TOKEN=$(echo $KAVO_SECRETS | base64 --decode | sed -n 's/^SECRET_API_TOKEN=//p')
SLACK_TOKEN=$(echo $KAVO_SECRETS | base64 --decode | sed -n 's/^SLACK_TOKEN=//p')

SECRETS=$(kamal secrets fetch --adapter bitwarden --account [email protected] GITHUB_CONTAINER_REGISTRY_API_TOKEN)
GITHUB_CONTAINER_REGISTRY_API_TOKEN=$(kamal secrets extract GITHUB_CONTAINER_REGISTRY_API_TOKEN $SECRETS)
8 changes: 8 additions & 0 deletions .kavo.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"$schema": "https://unpkg.com/[email protected]/schemas/config.json",
"keyring": "https://raw.githubusercontent.com/datocms/kavo-keyring/main/keyring.json",
"recipients": {
"type": "all"
},
"files": [".env"]
}
39 changes: 39 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# taken from https://docs.astro.build/en/recipes/docker/#multi-stage-build-using-ssr

FROM node:20 AS base

ENV INSIDE_DOCKER=true

WORKDIR /app

COPY package.json package-lock.json ./

FROM base AS prod-deps
RUN npm install --omit=dev

FROM base AS build-deps
RUN npm install

FROM build-deps AS build
COPY . .

RUN --mount=type=secret,id=DATOCMS_API_TOKEN \
npx gql.tada generate schema https://graphql.datocms.com --header "X-Exclude-Invalid: true" --header "Authorization: $(cat /run/secrets/DATOCMS_API_TOKEN)"

RUN --mount=type=secret,id=RECAPTCHA_KEY \
RECAPTCHA_KEY=$(cat /run/secrets/RECAPTCHA_KEY) \
npm run build

FROM base AS runtime
COPY --from=prod-deps /app/node_modules ./node_modules
COPY --from=build /app/dist ./dist

RUN --mount=type=secret,id=FASTLY_SERVICE_ID \
--mount=type=secret,id=FASTLY_KEY \
curl -i -X POST "https://api.fastly.com/service/$(cat /run/secrets/FASTLY_SERVICE_ID)/purge_all" -H "Fastly-Key: $(cat /run/secrets/FASTLY_KEY)" -H "Accept: application/json"

ENV HOST=0.0.0.0
ENV PORT=4321
EXPOSE 4321

CMD node ./dist/server/entry.mjs
2 changes: 1 addition & 1 deletion astro.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ export default defineConfig({
access: 'public',
}),
},
validateSecrets: true,
validateSecrets: false,
},
},
devToolbar: {
Expand Down
Loading

0 comments on commit 2d78027

Please sign in to comment.