initial attempt at IaC and CI/CD
This commit is contained in:
parent
597c7e4e0b
commit
820b251c61
11 changed files with 355 additions and 1 deletions
70
.github/workflows/app.yml
vendored
Normal file
70
.github/workflows/app.yml
vendored
Normal 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
33
.github/workflows/infra.yml
vendored
Normal 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
33
.vscode/launch.json
vendored
|
@ -4,7 +4,7 @@
|
||||||
{
|
{
|
||||||
"type": "bun",
|
"type": "bun",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"name": "Debug Bun",
|
"name": "Start dev",
|
||||||
// The path to a JavaScript or TypeScript file to run.
|
// The path to a JavaScript or TypeScript file to run.
|
||||||
"program": "${file}",
|
"program": "${file}",
|
||||||
// The arguments to pass to the program, if any.
|
// 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.
|
// Unlike `args`, these are passed to the executable itself, not the program.
|
||||||
"runtimeArgs": [],
|
"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",
|
"type": "bun",
|
||||||
"request": "attach",
|
"request": "attach",
|
||||||
|
|
32
Dockerfile
Normal file
32
Dockerfile
Normal 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
90
infrastructure/app.bicep
Normal 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'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
11
infrastructure/bicepconfig.json
Normal file
11
infrastructure/bicepconfig.json
Normal 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
46
infrastructure/main.bicep
Normal 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
|
||||||
|
}
|
||||||
|
}
|
11
infrastructure/monitoring.bicep
Normal file
11
infrastructure/monitoring.bicep
Normal 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: {}
|
||||||
|
// }
|
6
infrastructure/params/prod.bicepparam
Normal file
6
infrastructure/params/prod.bicepparam
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
using '../main.bicep'
|
||||||
|
|
||||||
|
param locationAbbreviation = 'euw'
|
||||||
|
param location = 'westeurope'
|
||||||
|
param environment = 'prd'
|
||||||
|
param projectName = 'calque'
|
16
infrastructure/registry.bicep
Normal file
16
infrastructure/registry.bicep
Normal 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
|
8
infrastructure/types.bicep
Normal file
8
infrastructure/types.bicep
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
@export()
|
||||||
|
type Context = {
|
||||||
|
locationAbbreviation: string
|
||||||
|
location: string
|
||||||
|
environment: string
|
||||||
|
projectName: string
|
||||||
|
deployedAt: string
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue