Skip to content

Commit

Permalink
feat: migrate from private GitHub repository (#1)
Browse files Browse the repository at this point in the history
  • Loading branch information
gilles-gosuin authored Oct 31, 2023
1 parent d25161e commit 232e04d
Show file tree
Hide file tree
Showing 171 changed files with 6,818 additions and 0 deletions.
22 changes: 22 additions & 0 deletions .github/dependabot.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
- package-ecosystem: "docker"
directory: "/app"
schedule:
interval: "weekly"
- package-ecosystem: "gradle"
directory: "/app"
schedule:
interval: "weekly"
- package-ecosystem: "terraform"
directory: "/infrastructure/stacks/app"
schedule:
interval: "weekly"
- package-ecosystem: "terraform"
directory: "/infrastructure/stacks/infra"
schedule:
interval: "weekly"
66 changes: 66 additions & 0 deletions .github/workflows/release.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
name: Release
on:
push:
branches:
- main
- beta
jobs:
release:
name: Release
runs-on: ubuntu-latest
env:
TF_PLUGIN_CACHE_DIR: ${{ github.workspace }}/.terraform.d/plugin-cache
steps:

- name: Check out source code
uses: actions/checkout@v4

- name: Init terraform cache
run: mkdir -p $TF_PLUGIN_CACHE_DIR

- name: Setup terraform cache
uses: actions/cache@v3
with:
path: ${{ env.TF_PLUGIN_CACHE_DIR }}
key: ${{ runner.os }}-terraform-${{ hashFiles('**/.terraform.lock.hcl') }}
restore-keys: ${{ runner.os }}-terraform-

- name: Set up Terraform
uses: hashicorp/setup-terraform@v2
with:
terraform_version: 1.6.2
terraform_wrapper: false

- name: Set up Cloud SDK
uses: google-github-actions/setup-gcloud@v1

- name: Authenticate to Google Cloud
uses: google-github-actions/auth@v1
with:
export_environment_variables: true
service_account: terraform@${{ vars.GOOGLE_PROJECT }}.iam.gserviceaccount.com
workload_identity_provider: projects/${{ vars.GOOGLE_PROJECT_NUMBER }}/locations/global/workloadIdentityPools/github-actions-workflows/providers/github-actions

- name: Install semantic-release exec plugin
run: npm install --save-dev @semantic-release/exec

- name: Release
env:
ARTIFACT_REGISTRY_LOCATION: ${{ vars.ARTIFACT_REGISTRY_LOCATION }}
CREDENTIALS: ${{ secrets.CREDENTIALS }}
DEV_BUCKET: ${{ vars.DEV_BUCKET }}
DOMAIN_NAME: ${{ vars.DOMAIN_NAME }}
FIRESTORE_LOCATION: ${{ vars.FIRESTORE_LOCATION }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GOOGLE_PROJECT: ${{ vars.GOOGLE_PROJECT }}
GOOGLE_REGION: ${{ vars.GOOGLE_REGION }}
GOOGLE_ZONE: ${{ vars.GOOGLE_ZONE }}
PROD_BUCKET: ${{ vars.PROD_BUCKET }}
SEND_GRID_API_KEY: ${{ secrets.SEND_GRID_API_KEY }}
run: npx semantic-release@22

permissions:
contents: write
id-token: write
issues: write
pull-requests: write
18 changes: 18 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# macOS
.DS_Store

# IntelliJ IDEA
.idea/
out/
*.iml

# Gradle
.gradle/
build/

# Terraform
.terraform/
tfplan

# Custom
/assets/
12 changes: 12 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
repos:
- repo: https://github.com/antonbabenko/pre-commit-terraform
rev: 'v1.83.5' # Get the latest from: https://github.com/antonbabenko/pre-commit-terraform/releases
hooks:
- id: terraform_providers_lock
files: "^infrastructure/stacks/(.)+/terraform.tf$"
args:
- --args=-platform=windows_amd64
- --args=-platform=darwin_amd64
- --args=-platform=darwin_arm64
- --args=-platform=linux_amd64
- --args=-platform=linux_arm64
18 changes: 18 additions & 0 deletions .releaserc.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
branches:
- name: main
channel: prod
- name: beta
channel: dev
prerelease: true

plugins: [
"@semantic-release/commit-analyzer",
[
"@semantic-release/exec",
{
"publishCmd": "make VERSION=${nextRelease.version} ${nextRelease.channel}"
}
],
"@semantic-release/release-notes-generator",
"@semantic-release/github"
]
133 changes: 133 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
DMUA_HOME=$(HOME)/.dmua
DMUA_SECRETS=$(DMUA_HOME)/secrets
DMUA_VARIABLES=$(DMUA_HOME)/variables

GOOGLE_PROJECT ?= $(shell cat "$(DMUA_VARIABLES)/GOOGLE_PROJECT")
VERSION ?= $(shell git describe --tags --always --first-parent)

ARTIFACT_REGISTRY_LOCATION ?= $(shell cat "$(DMUA_VARIABLES)/ARTIFACT_REGISTRY_LOCATION")
ARTIFACT_REGISTRY = $(ARTIFACT_REGISTRY_LOCATION)-docker.pkg.dev
ARTIFACT_REPOSITORY = common
CREDENTIALS ?= $(shell cat "$(DMUA_SECRETS)/CREDENTIALS")
DEV_BUCKET ?= $(shell cat "$(DMUA_VARIABLES)/DEV_BUCKET")
DOCKER_TAG = $(ARTIFACT_REGISTRY)/$(GOOGLE_PROJECT)/$(ARTIFACT_REPOSITORY)/website:$(VERSION)
DOMAIN_NAME ?= $(shell cat "$(DMUA_VARIABLES)/DOMAIN_NAME")
FIRESTORE_LOCATION ?= $(shell cat "$(DMUA_VARIABLES)/FIRESTORE_LOCATION")
GOOGLE_REGION ?= $(shell cat "$(DMUA_VARIABLES)/GOOGLE_REGION")
GOOGLE_ZONE ?= $(shell cat "$(DMUA_VARIABLES)/GOOGLE_ZONE")
PROD_BUCKET ?= $(shell cat "$(DMUA_VARIABLES)/PROD_BUCKET")
SEND_GRID_API_KEY ?= $(shell cat "$(DMUA_SECRETS)/SEND_GRID_API_KEY")

export GOOGLE_PROJECT
export GOOGLE_REGION
export GOOGLE_ZONE

TERRAFORM_INFRA_VARS = \
-var 'artifact_registry_location=$(ARTIFACT_REGISTRY_LOCATION)' \
-var 'artifact_repository=$(ARTIFACT_REPOSITORY)' \
-var 'credentials=$(CREDENTIALS)' \
-var 'firestore_location=$(FIRESTORE_LOCATION)' \
-var 'send_grid_api_key=$(SEND_GRID_API_KEY)'

TERRAFORM_APP_VARS = \
-var 'artifact_registry_location=$(ARTIFACT_REGISTRY_LOCATION)' \
-var 'artifact_repository=$(ARTIFACT_REPOSITORY)' \
-var 'docker_tag=$(DOCKER_TAG)' \
-var 'location=$(GOOGLE_REGION)'

TERRAFORM_DEV_APP_VARS = \
$(TERRAFORM_APP_VARS) \
-var-file=variables/development.tfvars \
-var 'bucket_name=$(DEV_BUCKET)'

TERRAFORM_PROD_APP_VARS = \
$(TERRAFORM_APP_VARS) \
-var-file=variables/production.tfvars \
-var 'bucket_name=$(PROD_BUCKET)' \
-var 'cors_origins_list=["https://$(DOMAIN_NAME)"]' \
-var 'domain_name=$(DOMAIN_NAME)'

TERRAFORM_INFRA_GLOBAL_OPTIONS = -chdir=infrastructure/stacks/infra
TERRAFORM_APP_GLOBAL_OPTIONS = -chdir=infrastructure/stacks/app

TERRAFORM_BACKEND_OPTIONS = -backend-config=bucket=terraform-state-$(GOOGLE_PROJECT)
TERRAFORM_INFRA_BACKEND_OPTIONS = $(TERRAFORM_BACKEND_OPTIONS) -backend-config=prefix=common/infra
TERRAFORM_DEV_APP_BACKEND_OPTIONS = $(TERRAFORM_BACKEND_OPTIONS) -backend-config=prefix=development/app
TERRAFORM_PROD_APP_BACKEND_OPTIONS = $(TERRAFORM_BACKEND_OPTIONS) -backend-config=prefix=production/app

TERRAFORM_APPLY_OPTIONS = -input=false -auto-approve
TERRAFORM_INIT_OPTIONS = -input=false -reconfigure -upgrade
TERRAFORM_PLAN_OPTIONS = -input=false
TERRAFORM_UNLOCK_OPTIONS = -force ${LOCK_ID}

run:
@cd app && ./gradlew run

push:
@cd app && docker build -t $(DOCKER_TAG) .
@gcloud auth configure-docker $(ARTIFACT_REGISTRY) --quiet
@docker push $(DOCKER_TAG)

bootstrap:
@bash infrastructure/bootstrap/install.sh

init-infra:
@terraform $(TERRAFORM_INFRA_GLOBAL_OPTIONS) init $(TERRAFORM_INIT_OPTIONS) $(TERRAFORM_INFRA_BACKEND_OPTIONS) $(TERRAFORM_INFRA_VARS)

plan-infra:
@terraform $(TERRAFORM_INFRA_GLOBAL_OPTIONS) plan $(TERRAFORM_PLAN_OPTIONS) $(TERRAFORM_INFRA_VARS)

apply-infra:
@terraform $(TERRAFORM_INFRA_GLOBAL_OPTIONS) apply $(TERRAFORM_APPLY_OPTIONS) $(TERRAFORM_INFRA_VARS)

init-dev:
@terraform $(TERRAFORM_APP_GLOBAL_OPTIONS) init $(TERRAFORM_INIT_OPTIONS) $(TERRAFORM_DEV_APP_BACKEND_OPTIONS) $(TERRAFORM_DEV_APP_VARS)

init-prod:
@terraform $(TERRAFORM_APP_GLOBAL_OPTIONS) init $(TERRAFORM_INIT_OPTIONS) $(TERRAFORM_PROD_APP_BACKEND_OPTIONS) $(TERRAFORM_PROD_APP_VARS)

plan-dev:
@terraform $(TERRAFORM_APP_GLOBAL_OPTIONS) plan $(TERRAFORM_PLAN_OPTIONS) $(TERRAFORM_DEV_APP_VARS)

plan-prod:
@terraform $(TERRAFORM_APP_GLOBAL_OPTIONS) plan $(TERRAFORM_PLAN_OPTIONS) $(TERRAFORM_PROD_APP_VARS)

apply-dev:
@terraform $(TERRAFORM_APP_GLOBAL_OPTIONS) apply $(TERRAFORM_APPLY_OPTIONS) $(TERRAFORM_DEV_APP_VARS)

apply-prod:
@terraform $(TERRAFORM_APP_GLOBAL_OPTIONS) apply $(TERRAFORM_APPLY_OPTIONS) $(TERRAFORM_PROD_APP_VARS)

unlock-infra:
@terraform $(TERRAFORM_INFRA_GLOBAL_OPTIONS) force-unlock $(TERRAFORM_UNLOCK_OPTIONS)

unlock-app:
@terraform $(TERRAFORM_APP_GLOBAL_OPTIONS) force-unlock $(TERRAFORM_UNLOCK_OPTIONS)

dev:
$(MAKE) init-infra
$(MAKE) apply-infra
$(MAKE) push
$(MAKE) init-dev
$(MAKE) apply-dev

prod:
$(MAKE) init-infra
$(MAKE) apply-infra
$(MAKE) init-prod
$(MAKE) apply-prod

tear-down:
@bash infrastructure/bootstrap/uninstall.sh

dev-assets-to-local:
@gsutil -m rsync -r -d gs://$(DEV_BUCKET) assets/

prod-assets-to-local:
@gsutil -m rsync -r -d gs://$(PROD_BUCKET) assets/

local-assets-to-dev:
@gsutil -m rsync -x ".DS_Store" -r -d assets/ gs://$(DEV_BUCKET)

dev-assets-to-prod:
@gsutil -m rsync -r -d gs://$(DEV_BUCKET) gs://$(PROD_BUCKET)
109 changes: 109 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
![Dessine-Moi un Alpaga](logo.png)

![](https://github.com/gilles-gosuin/dessine-moi-un-alpaga/actions/workflows/release.yaml/badge.svg)
[![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg?logo=gnu)](https://www.gnu.org/licenses/gpl-3.0)
[![Terraform](https://img.shields.io/badge/terraform-1.6.2-darkred.svg?logo=terraform)](http://gradle.org)
[![Gradle](https://img.shields.io/badge/gradle-8.4-darkgreen.svg?logo=gradle)](http://gradle.org)
[![GrralVM](https://img.shields.io/badge/graalvm-17.0.9-blue.svg?logo=openjdk)](http://graalvm.org)
[![Kotlin](https://img.shields.io/badge/kotlin-1.9.10-darkblue.svg?logo=kotlin)](http://kotlinlang.org)
[![Ktor](https://img.shields.io/badge/ktor-2.3.5-red.svg)](http://ktor.io)

# What is this?

An application for serving and managing [the website of our alpaca breeding farm 🦙](https://dessinemoiunalpaga.com).

# Prerequisites

The following is required to bootstrap the project:
* a Google Cloud organization
* a Google user account tht is `Organization Administrator` and has the `Owner` role on the project.

# Bootstrapping the Project

This project includes an interactive script that will prompt you for several configuration items and make sure
everything is set up for deploying the application to Google Cloud Run from your local box and from GitHub.

```shell
$ make bootstrap
```

Configuration will be saved in the `~/.dmua` directory.

The application uses Google
[Application Default Credentials](https://cloud.google.com/docs/authentication/application-default-credentials) and will
automatically detect the credentials for the user you are logged in with:

```shell
$ gcloud auth application-default login
```

Your are now all set to run the project locally:

```shell
$ make run
```

# Configuring GitHub Actions

Running the GitHub Actions workflows requires you to copy some secrets and variables to GitHub Actions.

Note that not all secrets and variables are required for successfully running GitHub Workflows; the complete list is
provided below.

## GitHub Actions Secrets

Values can be found in the corresponding files located in `~/.dmua/secrets`:

* `CREDENTIALS`
* `SEND_GRID_API_KEY`

## GitHub Actions Variables

Values can be found in the corresponding files located in `~/.dmua/variables`:

* `ARTIFACT_REGISTRY_LOCATION`
* `DEV_BUCKET`
* `DOMAIN_NAME`
* `FIRESTORE_LOCATION`
* `GOOGLE_PROJECT`
* `GOOGLE_PROJECT_NAME`
* `GOOGLE_PROJECT_NUMBER`
* `GOOGLE_REGION`
* `GOOGLE_ZONE`
* `PROD_BUCKET`

# Contributing

All changes must take place on the `beta` branch. Every commit will trigger a semantic pre-release, tag the commit
accordingly, publish a GitHub pre-release and deploy it to the development environment.

Once the development environment is in a satisfactory state, create a pull request to the main branch and merge it. This
will trigger a semantic release, tag the commit accordingly, publish a GitHub release and deploy it to the production
environment.

# Isn't this overkill? Are you crazy?

No. My mother had me tested.

## Functional Requirements

The main functional requirement behind its design is that it must include an API to CRUD most of the contents of the
website at runtime:
* animals
* news articles
* photos
* factsheets
* etc.

## Non-Functional Requirements

The first non-functional driver behind its design is that it should minimize application startup time and resource usage,
so that it can shine when deployed on [Google Cloud Run](https://cloud.google.com/run), thus reducing its carbon footprint (and, as a side
benefit, its price) as much as possible, [in a measurable way](https://console.cloud.google.com/carbon).

The second non-functional driver is that I should be having fun working on it 😊

Coming from a JVM background, I opted for [Kotlin](https://kotlinlang.org), compiled into a native binary by
[GraalVM](https://graalvm.org).

[Ktor](https://ktor.io) was the next logical choice, as most of its features can be compiled into native binaries quite easily.
7 changes: 7 additions & 0 deletions app/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
**/*

!gradle/**
!src/**
!build.gradle.kts
!gradlew
!gradlew.bat
Loading

0 comments on commit 232e04d

Please sign in to comment.