-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
#72 merge repos
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
# 8. Deploy on fly.io | ||
|
||
Date: 2023-12-10 | ||
|
||
## Status | ||
|
||
Accepted | ||
|
||
## Context | ||
|
||
We want to make the statistic-service available online, so we need to either host a service on-premise or in the cloud. | ||
|
||
## Decision | ||
|
||
Deploy the (PRODUCTION) service on [fly.io](https://fly.io), an affordable cloud service provider with a nice developer experience. | ||
|
||
## Consequences | ||
|
||
* some secrets (API-tokens) need to be configured via the fly.io command line tool. | ||
* for development, the flyctl utility needs to be installed. See their [documentation](https://fly.io/docs/speedrun/) for details. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
# flyctl launch added from .gitignore | ||
# If you prefer the allow list template instead of the deny list, see community template: | ||
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore | ||
# | ||
# Binaries for programs and plugins | ||
**/*.exe | ||
**/*.exe~ | ||
**/*.dll | ||
**/*.so | ||
**/*.dylib | ||
|
||
# Test binary, built with `go test -c` | ||
**/*.test | ||
|
||
# Output of the go coverage tool, specifically when used with LiteIDE | ||
**/*.out | ||
|
||
# Dependency directories (remove the comment below to include it) | ||
# vendor/ | ||
|
||
# Go workspace file | ||
**/go.work | ||
**/.DS_Store | ||
|
||
# flyctl launch added from .idea/.gitignore | ||
# Default ignored files | ||
.idea/shelf | ||
.idea/workspace.xml | ||
# Editor-based HTTP Client requests | ||
.idea/httpRequests | ||
# Datasource local storage ignored files | ||
.idea/dataSources | ||
.idea/dataSources.local.xml | ||
fly.toml |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
name: arc42 site usage statistics | ||
on: | ||
push: | ||
branches: | ||
- main | ||
jobs: | ||
deploy: | ||
name: Deploy app | ||
runs-on: ubuntu-latest | ||
steps: | ||
- uses: actions/checkout@v3 | ||
- uses: superfly/flyctl-actions/setup-flyctl@master | ||
- run: flyctl deploy --remote-only | ||
env: | ||
FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }} | ||
GITHUB_API_KEY: ${{ secrets.GRAPHQL_API_TOKEN }} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
# If you prefer the allow list template instead of the deny list, see community template: | ||
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore | ||
# | ||
# Binaries for programs and plugins | ||
*.exe | ||
*.exe~ | ||
*.dll | ||
*.so | ||
*.dylib | ||
|
||
# Test binary, built with `go test -c` | ||
*.test | ||
|
||
# Output of the go coverage tool, specifically when used with LiteIDE | ||
*.out | ||
|
||
# Dependency directories (remove the comment below to include it) | ||
# vendor/ | ||
|
||
# Go workspace file | ||
go.work | ||
.DS_Store | ||
set-plausible-api-key.sh | ||
set-api-keys.sh |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
# syntax=docker/dockerfile:1 | ||
|
||
FROM golang:1.21 | ||
|
||
# Set destination for COPY | ||
WORKDIR /app | ||
|
||
# Download Go modules | ||
COPY go.mod go.sum ./ | ||
RUN go mod download | ||
|
||
# Copy the source code. Note the slash at the end, as explained in | ||
# https://docs.docker.com/engine/reference/builder/#copy | ||
COPY *.go ./ | ||
COPY internal/ ./internal/ | ||
|
||
|
||
# Build | ||
RUN CGO_ENABLED=0 GOOS=linux go build -o /arc42-stats | ||
|
||
# To bind to a TCP port, runtime parameters must be supplied to the docker command. | ||
# But we can (optionally) document in the Dockerfile what ports | ||
# the application is going to listen on by default. | ||
# https://docs.docker.com/engine/reference/builder/#expose | ||
EXPOSE 8043 | ||
|
||
# Run | ||
CMD [ "/arc42-stats" ] |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
<!DOCTYPE html> | ||
<html lang="en"> | ||
<head> | ||
<meta charset="UTF-8"> | ||
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||
<title>Load shields.io badges</title> | ||
</head> | ||
<body> | ||
|
||
|
||
<h1>Welcome to shields.io badges for arc42 status-page</h1> | ||
|
||
|
||
<img src="https://img.shields.io/badge/open_issues-CEA41E"/> | ||
<img src="https://img.shields.io/badge/20-open_issues-CEA41E"/> | ||
<img src="https://img.shields.io/badge/20+-open_issues-CEA41E"/> | ||
|
||
|
||
<img src="https://img.shields.io/badge/issues-6-BDB76B"/> | ||
<img src="https://img.shields.io/badge/issues-...-BDB76B"/> | ||
|
||
<h2>Bugs</h2> | ||
<img src="https://img.shields.io/badge/bugs-1-DC143C"/> | ||
<img src="https://img.shields.io/badge/bugs-2-DC143C"/> | ||
<img src="https://img.shields.io/badge/bugs-3-DC143C"/> | ||
<img src="https://img.shields.io/badge/bugs-4-DC143C"/> | ||
<img src="https://img.shields.io/badge/bugs-5-DC143C"/> | ||
<img src="https://img.shields.io/badge/bugs-6-DC143C"/> | ||
<img src="https://img.shields.io/badge/bugs-7-DC143C"/> | ||
<img src="https://img.shields.io/badge/bugs-8-DC143C"/> | ||
<img src="https://img.shields.io/badge/bugs-9-DC143C"/> | ||
<img src="https://img.shields.io/badge/bugs-10-DC143C"/> | ||
<img src="https://img.shields.io/badge/bugs-11-DC143C"/> | ||
<img src="https://img.shields.io/badge/bugs-12-DC143C"/> | ||
<img src="https://img.shields.io/badge/bugs-13-DC143C"/> | ||
<img src="https://img.shields.io/badge/bugs-14-DC143C"/> | ||
<img src="https://img.shields.io/badge/bugs-15-DC143C"/> | ||
<img src="https://img.shields.io/badge/bugs-16-DC143C"/> | ||
<img src="https://img.shields.io/badge/bugs-17-DC143C"/> | ||
<img src="https://img.shields.io/badge/bugs-18-DC143C"/> | ||
<img src="https://img.shields.io/badge/bugs-19-DC143C"/> | ||
<img src="https://img.shields.io/badge/bugs-20-DC143C"/> | ||
|
||
</body> | ||
</html> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,139 @@ | ||
package main | ||
|
||
// Saves svg-badges from shields.io to local storage. | ||
// | ||
// These svg files follow this naming schema: | ||
// <nr>-issues.svg (e.g. `13-issues.svg) | ||
// <nr>-bugs.svg (`3-bugs.svg`) | ||
// If there are more issues than `badge.LocalBadgeSvgThreshold`, | ||
// the naming is `20+issues.svg`. | ||
|
||
// we download these badges from shields.io, from URLs like this: | ||
// https://img.shields.io/badge/open_issues-19-BDB76B | ||
import ( | ||
"arc42-status/internal/badge" | ||
"github.com/rs/zerolog" | ||
"github.com/rs/zerolog/log" | ||
"io" | ||
"net/http" | ||
"os" | ||
"strconv" | ||
) | ||
|
||
// SVGBadgePath is the constant path to the directory where SVG files for badges are stored. | ||
const SVGBadgePath = "./svgs/" | ||
|
||
const issuesColor = "CEA41E" | ||
const bugsColor = "DC143C" | ||
|
||
const badgeDownloadURLPrefix = "https://img.shields.io/badge/" | ||
|
||
// svgFileNameForKindOf creates the filename for the downloaded issue-svg files. | ||
// These are required both for the downloading process AND for creating the URLs in the final output HTML | ||
func svgFileNameForKindOf(kindOf string, count int) string { | ||
switch kindOf { | ||
case badge.IssueName: | ||
return strconv.Itoa(count) + badge.IssueBadgeFileNameSuffix | ||
case badge.BugName: | ||
return strconv.Itoa(count) + badge.BugBadgeFileNameSuffix | ||
default: | ||
log.Error().Msgf("error creating filename for count %d and kindOf %s", count, kindOf) | ||
return "_error-" + strconv.Itoa(count) | ||
} | ||
} | ||
|
||
func badgeColorForKindOf(kindOf string) string { | ||
switch kindOf { | ||
case badge.IssueName: | ||
return issuesColor | ||
case badge.BugName: | ||
return bugsColor | ||
default: | ||
return issuesColor | ||
} | ||
} | ||
|
||
// nrOfBugsIssuesShown returns the infix needed to create the shields.io URL | ||
func nrOfBugsIssuesShown(count int, kindOf string) string { | ||
if count == 1 { | ||
// note: Singular | ||
return "open_" + kindOf + "-1-" | ||
} else if (count == 0) || (count <= badge.LocalBadgeSvgThreshold) { | ||
// count + Plural | ||
return "open_" + kindOf + "s-" + strconv.Itoa(count) + "-" | ||
} else { | ||
// 20+ | ||
return "open_" + kindOf + "s-" + strconv.Itoa(badge.LocalBadgeSvgThreshold) + "+-" | ||
} | ||
} | ||
|
||
func openIssueSVGBadgeDownloadURL(count int, kindOf string) string { | ||
var infix = nrOfBugsIssuesShown(count, kindOf) | ||
return badgeDownloadURLPrefix + infix + badgeColorForKindOf(kindOf) | ||
} | ||
|
||
func createSVGBadgeDirIfNotPresent(dirName string) { | ||
|
||
if _, err := os.Stat(dirName); os.IsNotExist(err) { | ||
errDir := os.MkdirAll(dirName, 0755) | ||
if errDir != nil { | ||
log.Fatal().Msg(errDir.Error()) | ||
} else { | ||
log.Info().Msgf("directory %s created", dirName) | ||
} | ||
|
||
} | ||
} | ||
|
||
// Function to download and save SVG file | ||
func downloadSVG(url string, count int, kindOf string) error { | ||
resp, err := http.Get(url) | ||
if err != nil { | ||
return err | ||
} | ||
defer resp.Body.Close() | ||
|
||
fileName := svgFileNameForKindOf(kindOf, count) | ||
log.Info().Msgf("filename is %s", fileName) | ||
|
||
file, err := os.Create(SVGBadgePath + fileName) | ||
if err != nil { | ||
return err | ||
} | ||
defer file.Close() | ||
|
||
_, err = io.Copy(file, resp.Body) | ||
return err | ||
} | ||
|
||
func init() { | ||
// Configure the global logger to write to console/stdout and add file and line number | ||
output := zerolog.ConsoleWriter{Out: os.Stdout, TimeFormat: zerolog.TimeFormatUnix} | ||
log.Logger = zerolog.New(output).With().Timestamp().Caller().Logger() | ||
} | ||
|
||
func downloadSVGBadgesForKindof(kindOf string) { | ||
log.Info().Msgf("starting to download %ss", kindOf) | ||
|
||
// Download and save each SVG file | ||
for count := 1; count <= badge.LocalBadgeSvgThreshold+1; count++ { | ||
|
||
// first, load badge for issue | ||
url := openIssueSVGBadgeDownloadURL(count, kindOf) | ||
log.Info().Msgf("loading badge-SVG for %d %ss from %s", count, kindOf, url) | ||
err := downloadSVG(url, count, kindOf) | ||
if err != nil { | ||
panic(err) | ||
} | ||
} | ||
} | ||
|
||
func main() { | ||
// create SVGBadge directory, if it does not exist, | ||
// so we have a filesystem location to store SVG files | ||
createSVGBadgeDirIfNotPresent(SVGBadgePath) | ||
|
||
downloadSVGBadgesForKindof(badge.IssueName) | ||
downloadSVGBadgesForKindof(badge.BugName) | ||
|
||
} |