initial attempt at IaC and CI/CD

This commit is contained in:
Chris Kruining 2024-11-14 16:22:11 +01:00
parent 597c7e4e0b
commit 820b251c61
No known key found for this signature in database
GPG key ID: EB894A3560CCCAD2
11 changed files with 355 additions and 1 deletions

70
.github/workflows/app.yml vendored Normal file
View file

@ -0,0 +1,70 @@
name: Deploy App
on:
push:
paths-ignore:
- infrastructure/**
branches:
- main
pull_request:
paths-ignore:
- infrastructure/**
types: [opened, synchronize, reopened, closed]
branches:
- main
env:
IMAGE_NAME: calque-app
permissions:
id-token: write
contents: read
jobs:
versionize:
name: Versionize
if: github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.action != 'closed')
runs-on: ubuntu-latest
outputs:
semver: ${{ steps.gitversion.outputs.SemVer }}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install GitVersion
uses: gittools/actions/gitversion/setup@v1.1.1
with:
versionSpec: "5.x"
- name: Determine Version
id: gitversion
uses: gittools/actions/gitversion/execute@v1.1.1
with:
useConfigFile: true
build_and_publish:
name: Build & Publish
if: github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.action != 'closed')
runs-on: ubuntu-latest
needs: versionize
steps:
- uses: actions/checkout@v4
- name: Build container images
working-directory: src
run: |
docker build . --file Dockerfile --tag ${{ secrets.ACR_LOGIN_SERVER }}/$IMAGE_NAME:${{needs.versionize.outputs.semver}}
docker build . --file Dockerfile --tag ${{ secrets.ACR_LOGIN_SERVER }}/$IMAGE_NAME:latest
- name: Login to ACR
uses: azure/docker-login@v2
with:
login-server: ${{ secrets.ACR_LOGIN_SERVER }}
username: ${{ secrets.ACR_USERNAME }}
password: ${{ secrets.ACR_PASSWORD }}
- name: Push container images
working-directory: src
run: |
docker push ${{ secrets.ACR_LOGIN_SERVER }}/$IMAGE_NAME:${{needs.versionize.outputs.semver}}
docker push ${{ secrets.ACR_LOGIN_SERVER }}/$IMAGE_NAME:latest

33
.github/workflows/infra.yml vendored Normal file
View file

@ -0,0 +1,33 @@
name: Deploy infrastructure
on:
push:
paths:
- infrastructure/**
branches:
- main
pull_request:
paths:
- infrastructure/**
types: [opened, synchronize, reopened, closed]
branches:
- main
permissions:
id-token: write
contents: read
jobs:
production:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
sparse-checkout: |
infrastructure
- name: Deploy bicep
uses: Azure/cli@v2
with:
inlineScript: |
az deployment sub create --location westeurope --template-file infrastructure/main.bicep --parameters infrastructure/params/prod.bicepparam

33
.vscode/launch.json vendored
View file

@ -4,7 +4,7 @@
{
"type": "bun",
"request": "launch",
"name": "Debug Bun",
"name": "Start dev",
// The path to a JavaScript or TypeScript file to run.
"program": "${file}",
// The arguments to pass to the program, if any.
@ -29,6 +29,37 @@
// Unlike `args`, these are passed to the executable itself, not the program.
"runtimeArgs": [],
},
{
"type": "bun",
"request": "launch",
"name": "Run tests",
// The path to a JavaScript or TypeScript file to run.
"program": "${file}",
// The arguments to pass to the program, if any.
"args": [],
// The working directory of the program.
"cwd": "${workspaceFolder}",
// The environment variables to pass to the program.
"env": {},
// If the environment variables should not be inherited from the parent process.
"strictEnv": false,
// If the program should be run in watch mode.
// This is equivalent to passing `--watch` to the `bun` executable.
// You can also set this to "hot" to enable hot reloading using `--hot`.
"watchMode": false,
// If the debugger should stop on the first line of the program.
"stopOnEntry": false,
// If the debugger should be disabled. (for example, breakpoints will not be hit)
"noDebug": false,
// The path to the `bun` executable, defaults to your `PATH` environment variable.
"runtime": "bun",
// The arguments to pass to the `bun` executable, if any.
// Unlike `args`, these are passed to the executable itself, not the program.
"runtimeArgs": [
"--bun",
"test"
],
},
{
"type": "bun",
"request": "attach",

32
Dockerfile Normal file
View file

@ -0,0 +1,32 @@
FROM oven/bun:1 as base
WORKDIR /usr/src/app
FROM base AS install
RUN mkdir -p /temp/dev
COPY package.json bun.lockb /temp/dev
RUN cd /temp/dev && bun install --frozen-lockfile
RUN mkdir -p /temp/prod
COPY package.json bun.lockb /temp/prod/
RUN cd /temp/prod && bun install --frozen-lockfile --production
FROM base AS prerelease
COPY --from=install /temp/dev/node_modules node_modules
COPY . .
ENV NODE_ENV=production
ENV SERVER_PRESET=bun
RUN bun test
RUN chmod +x node_modules/.bin/*
RUN bun run build
FROM base AS release
COPY --from=install /temp/prod/node_modules node_modules
COPY --from=prerelease /usr/src/app/bun.lockb .
COPY --from=prerelease /usr/src/app/package.json .
COPY --from=prerelease /usr/src/app/.vinxi .vinxi
COPY --from=prerelease /usr/src/app/.output .output
USER bun
EXPOSE 3000/tcp
ENTRYPOINT [ "bun", "run", "start" ]

90
infrastructure/app.bicep Normal file
View file

@ -0,0 +1,90 @@
import { Context } from 'types.bicep'
targetScope = 'resourceGroup'
param context Context
param registry resource'Microsoft.ContainerRegistry/registries@2023-07-01'
var appName = 'app'
var version = 'latest'
resource environment 'Microsoft.App/managedEnvironments@2024-03-01' = {
name: 'acr-${context.locationAbbreviation}-${context.environment}-${context.projectName}'
location: context.location
properties: {
appLogsConfiguration: {
destination: 'azure-monitor'
}
}
}
resource app 'Microsoft.App/containerApps@2024-03-01' = {
name: 'acr-${context.locationAbbreviation}-${context.environment}-${context.projectName}-app'
location: context.location
identity: {
type: 'SystemAssigned'
}
properties: {
environmentId: environment.id
configuration: {
activeRevisionsMode: 'Single'
ingress: {
external: true
targetPort: 8080
transport: 'http2'
allowInsecure: false
traffic: [
{
weight: 100
latestRevision: true
}
]
corsPolicy: {
allowedOrigins: [
// 'https://localhost:3000'
'*'
]
allowCredentials: true
allowedHeaders: ['*']
allowedMethods: ['Get, POST']
maxAge: 0
}
}
registries: [
{
identity: 'system'
server: registry.properties.loginServer
}
]
}
template: {
containers: [
{
image: '${registry.properties.loginServer}/${context.projectName}-${appName}:${version}'
name: '${context.projectName}-${appName}'
resources: {
cpu: json('0.25')
memory: '0.5Gi'
}
}
]
scale: {
minReplicas: 1
maxReplicas: 2
rules: [
{
name: 'http-rule'
http: {
metadata: {
concurrentRequests: '50'
}
}
}
]
}
}
}
}

View file

@ -0,0 +1,11 @@
{
"experimentalFeaturesEnabled": {
"assertions": true,
"testFramework": true,
"extensibility": true,
"resourceDerivedTypes": true,
"resourceTypedParamsAndOutputs": true,
"sourceMapping": true,
"symbolicNameCodegen": true
}
}

46
infrastructure/main.bicep Normal file
View file

@ -0,0 +1,46 @@
import { Context } from 'types.bicep'
targetScope = 'subscription'
param locationAbbreviation string
param location string
param environment string
param projectName string
param deployedAt string = utcNow('yyyyMMdd')
var context = {
locationAbbreviation: locationAbbreviation
location: location
environment: environment
projectName: projectName
deployedAt: deployedAt
}
resource calqueResourceGroup 'Microsoft.Resources/resourceGroups@2024-07-01' = {
name: 'rg-${locationAbbreviation}-${environment}-${projectName}'
location: location
}
module monitoring 'monitoring.bicep' = {
name: 'monitoring'
scope: calqueResourceGroup
params: {
context: context
}
}
module registry 'registry.bicep' = {
name: 'registry'
scope: calqueResourceGroup
params: {
context: context
}
}
module app 'app.bicep' = {
name: 'app'
scope: calqueResourceGroup
params: {
context: context
}
}

View file

@ -0,0 +1,11 @@
import { Context } from 'types.bicep'
targetScope = 'resourceGroup'
param context Context
// resource monitoring 'Microsoft.___/___@___' = {
// name: 'acr-${context.locationAbbreviation}-${context.environment}-${context.projectName}'
// location: context.location
// properties: {}
// }

View file

@ -0,0 +1,6 @@
using '../main.bicep'
param locationAbbreviation = 'euw'
param location = 'westeurope'
param environment = 'prd'
param projectName = 'calque'

View file

@ -0,0 +1,16 @@
import { Context } from 'types.bicep'
targetScope = 'resourceGroup'
param context Context
resource registry 'Microsoft.ContainerRegistry/registries@2023-07-01' = {
name: 'acr-${context.locationAbbreviation}-${context.environment}-${context.projectName}'
location: context.location
sku: {
name: 'Basic'
}
properties: {}
}
output registry resource'Microsoft.ContainerRegistry/registries@2023-07-01' = registry

View file

@ -0,0 +1,8 @@
@export()
type Context = {
locationAbbreviation: string
location: string
environment: string
projectName: string
deployedAt: string
}