Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 72 additions & 8 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,18 @@ on:
workflow_dispatch:

jobs:
deploy_docker_jvm:
runs-on: ubuntu-22.04
build_jvm_matrix:
strategy:
matrix:
include:
- platform: linux/amd64
runner: ubuntu-24.04
- platform: linux/arm64
runner: ubuntu-24.04-arm
runs-on: ${{ matrix.runner }}
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4

Expand All @@ -18,14 +28,46 @@ jobs:
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Build and Push JVM Docker images
- name: Build and Push JVM Docker image for ${{ matrix.platform }}
run: |
make push-jvm
make push-jvm-platform PLATFORM=${{ matrix.platform }}
env:
GIT_TAG: ${{ github.ref }}

deploy_docker_native:
create_jvm_manifest:
needs: build_jvm_matrix
runs-on: ubuntu-22.04
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4

- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Create and Push JVM multi-platform manifest
run: |
make push-jvm-manifest
env:
GIT_TAG: ${{ github.ref }}

build_native_matrix:
strategy:
matrix:
include:
- platform: linux/amd64
runner: ubuntu-24.04
- platform: linux/arm64
runner: ubuntu-24.04-arm
runs-on: ${{ matrix.runner }}
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4

Expand All @@ -39,16 +81,38 @@ jobs:
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Build and Push Native Docker images
- name: Build and Push Native Docker image for ${{ matrix.platform }}
run: |
make push-native-platform PLATFORM=${{ matrix.platform }}
env:
GIT_TAG: ${{ github.ref }}

create_native_manifest:
needs: build_native_matrix
runs-on: ubuntu-22.04
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4

- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Create and Push Native multi-platform manifest
run: |
make push-native
make push-native-manifest
env:
GIT_TAG: ${{ github.ref }}

all:
name: Pushed All
if: always()
needs: [ deploy_docker_native, deploy_docker_jvm ]
needs: [ create_jvm_manifest, create_native_manifest ]
runs-on: ubuntu-22.04
steps:
- name: Validate required tests
Expand Down
2 changes: 1 addition & 1 deletion .sdkmanrc
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
# Enable auto-env through the sdkman_auto_env config
# Add key=value pairs of SDKs to use below
java=22-graalce
java=21.0.9-tem
29 changes: 29 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ IMG_NATIVE := ${NAME}:native-${TAG}
LATEST_JVM := ${NAME}:jvm-latest
LATEST_NATIVE := ${NAME}:native-latest
LATEST := ${NAME}:latest
PLATFORM ?= linux/amd64,linux/arm64

dependency-updates:
./gradlew dependencyUpdates \
Expand All @@ -23,6 +24,20 @@ build-jvm: init-docker
push-jvm:
DOCKER_EXTRA_ARGS="--push" $(MAKE) build-jvm

# Build and push for a single platform (used in matrix builds)
build-jvm-platform: init-docker
$(eval PLATFORM_TAG := $(shell echo ${PLATFORM} | tr '/' '-'))
docker buildx build --platform ${PLATFORM} -f ./src/main/docker/Dockerfile.jvm -t "${IMG_JVM}-${PLATFORM_TAG}" -t "${LATEST_JVM}-${PLATFORM_TAG}" ${DOCKER_EXTRA_ARGS} .

push-jvm-platform:
DOCKER_EXTRA_ARGS="--push" $(MAKE) build-jvm-platform

# Create and push multi-platform manifest combining platform-specific images
push-jvm-manifest:
docker buildx imagetools create -t "${IMG_JVM}" -t "${LATEST_JVM}" \
"${IMG_JVM}-linux-amd64" \
"${IMG_JVM}-linux-arm64"

build-jvm-local:
docker build -f ./src/main/docker/Dockerfile.jvm -t "${IMG_JVM}" -t "${LATEST_JVM}" .

Expand All @@ -35,6 +50,20 @@ build-native: init-docker
push-native:
DOCKER_EXTRA_ARGS="--push" $(MAKE) build-native

# Build and push for a single platform (used in matrix builds)
build-native-platform: init-docker
$(eval PLATFORM_TAG := $(shell echo ${PLATFORM} | tr '/' '-'))
docker buildx build --platform ${PLATFORM} -f ./src/main/docker/Dockerfile.native -t "${IMG_NATIVE}-${PLATFORM_TAG}" -t "${LATEST_NATIVE}-${PLATFORM_TAG}" -t "${LATEST}-${PLATFORM_TAG}" ${DOCKER_EXTRA_ARGS} .

push-native-platform:
DOCKER_EXTRA_ARGS="--push" $(MAKE) build-native-platform

# Create and push multi-platform manifest combining platform-specific images
push-native-manifest:
docker buildx imagetools create -t "${IMG_NATIVE}" -t "${LATEST_NATIVE}" -t "${LATEST}" \
"${IMG_NATIVE}-linux-amd64" \
"${IMG_NATIVE}-linux-arm64"

build-native-local:
docker build -f ./src/main/docker/Dockerfile.native -t "${IMG_NATIVE}" -t "${LATEST_NATIVE}" -t "${LATEST}" .

Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ than 10 MB of RAM, so it can be installed on under-powered servers.
> **NOTE**
>
> This used to be a Haskell project, that I switched to Kotlin. The code is still available on the [v1-haskell](https://github.com/alexandru/github-webhook-listener/tree/v1-haskell) branch.
> There's also an experimental Rust branch, see [v3-rust](https://github.com/alexandru/github-webhook-listener/tree/v3-rust).

## Setup

Expand Down
1 change: 1 addition & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ dependencies {
implementation(libs.kotlin.stdlib.jdk8)
implementation(libs.kotlin.test.junit)
implementation(libs.kotlinx.serialization.json)
implementation(libs.kotlinx.serialization.hocon)
implementation(libs.ktor.serialization.kotlinx.json)
implementation(libs.ktor.server.cio)
implementation(libs.ktor.server.core)
Expand Down
2 changes: 2 additions & 0 deletions settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ dependencyResolutionManagement {
.versionRef("kotlin")
library("kotlinx-serialization-json", "org.jetbrains.kotlinx", "kotlinx-serialization-json")
.versionRef("serialization")
library("kotlinx-serialization-hocon", "org.jetbrains.kotlinx", "kotlinx-serialization-hocon")
.versionRef("serialization")

// https://ktor.io/
plugin("ktor", "io.ktor.plugin")
Expand Down
95 changes: 86 additions & 9 deletions src/main/kotlin/org/alexn/hook/AppConfig.kt
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
@file:OptIn(ExperimentalSerializationApi::class)

package org.alexn.hook

import arrow.core.Either
import com.charleskorn.kaml.Yaml
import com.charleskorn.kaml.YamlConfiguration
import com.typesafe.config.ConfigFactory
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.Serializable
import kotlinx.serialization.hocon.Hocon
import kotlinx.serialization.hocon.decodeFromConfig
import java.io.File
import kotlin.time.Duration

Expand Down Expand Up @@ -36,16 +43,77 @@ data class AppConfig(
)

companion object {
fun parseYaml(string: String): AppConfig =
yamlParser.decodeFromString(
serializer(),
string,
)
fun parseFile(file: File) =
when (file.extension.lowercase()) {
"hocon", "conf" -> parseHocon(file)
"yaml", "yml" -> parseYaml(file)
else ->
Either.Left(
ConfigException(
"Unsupported configuration file format: ${file.extension}",
),
)
}

fun parseHocon(string: String): Either<ConfigException, AppConfig> =
try {
val r =
Hocon.decodeFromConfig(
serializer(),
ConfigFactory.parseString(string).resolve(),
)
Either.Right(r)
} catch (ex: Exception) {
Either.Left(
ConfigException(
"Failed to parse HOCON configuration",
ex,
),
)
}

fun parseHocon(file: File): Either<ConfigException, AppConfig> =
try {
val txt = file.readText()
parseHocon(txt)
} catch (ex: Exception) {
Either.Left(
ConfigException(
"Failed to read configuration file: ${file.absolutePath}",
ex,
),
)
}

fun parseYaml(string: String): Either<ConfigException, AppConfig> =
try {
Either.Right(
yamlParser.decodeFromString(
serializer(),
string,
),
)
} catch (ex: Exception) {
Either.Left(
ConfigException(
"Failed to parse YAML configuration",
ex,
),
)
}

fun parseYaml(file: File): AppConfig {
val txt = file.readText()
return parseYaml(txt)
}
fun parseYaml(file: File): Either<ConfigException, AppConfig> =
try {
val txt = file.readText()
parseYaml(txt)
} catch (ex: Exception) {
Either.Left(
ConfigException(
"Failed to read configuration file: ${file.absolutePath}",
ex,
),
)
}

private val yamlParser =
Yaml(
Expand All @@ -56,3 +124,12 @@ data class AppConfig(
)
}
}

/**
* Exception thrown when there is a configuration error,
* see [AppConfig].
*/
class ConfigException(
message: String,
cause: Throwable? = null,
) : Exception(message, cause)
5 changes: 3 additions & 2 deletions src/main/kotlin/org/alexn/hook/Main.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.alexn.hook

import arrow.continuations.SuspendApp
import arrow.core.getOrElse
import com.github.ajalt.clikt.core.CliktCommand
import com.github.ajalt.clikt.core.Context
import com.github.ajalt.clikt.core.main
Expand All @@ -17,8 +18,8 @@ class RunServer :

override fun run() =
SuspendApp {
val config = AppConfig.parseYaml(File(configPath))
startServer(config)
val config = AppConfig.parseFile(File(configPath))
startServer(config.getOrElse { throw it })
}
}

Expand Down
Loading
Loading