From acbc004baef8155f3042a810e640ea23575477fb Mon Sep 17 00:00:00 2001 From: Morten Andersen Date: Fri, 26 May 2023 07:48:16 +0100 Subject: [PATCH] fix: Add UTF-8 charset on slack POST call to remove the warning from our build logs. (#18) * fix: Add UTF-8 charset on slack POST call to remove the warning from our build logs. Also upgraded all dependencies * chore: Very basic readme file * ignore: Ktlint + autoformat --- .github/detekt.yml | 2 +- .github/workflows/publish.yaml | 5 ++- .github/workflows/pull_request.yml | 37 +++++++++++++++++++ .github/workflows/upgrade_gradle.yml | 14 +++++++ README.md | 10 ++++- build.gradle.kts | 21 ++++++----- settings.gradle.kts | 2 +- .../kotlin/com/monta/slack/notifier/Main.kt | 2 +- .../com/monta/slack/notifier/SlackClient.kt | 34 ++++++++--------- .../notifier/command/PublishSlackCommand.kt | 3 +- .../slack/notifier/model/GithubPushContext.kt | 22 +++++------ .../monta/slack/notifier/model/JobStatus.kt | 4 +- .../com/monta/slack/notifier/model/JobType.kt | 6 +-- .../monta/slack/notifier/model/SlackBlock.kt | 6 +-- .../slack/notifier/model/SlackMessage.kt | 10 ++--- .../notifier/service/PublishSlackService.kt | 10 ++--- .../notifier/util/GithubActionExtensions.kt | 9 +++-- .../monta/slack/notifier/util/HttpClient.kt | 14 ++++--- .../com/monta/slack/notifier/util/JsonUtil.kt | 2 +- .../monta/slack/notifier/util/StringUtils.kt | 5 +-- 20 files changed, 138 insertions(+), 80 deletions(-) create mode 100644 .github/workflows/pull_request.yml create mode 100644 .github/workflows/upgrade_gradle.yml diff --git a/.github/detekt.yml b/.github/detekt.yml index d9e65a4..a6b684f 100644 --- a/.github/detekt.yml +++ b/.github/detekt.yml @@ -783,4 +783,4 @@ style: active: true excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**' ] excludeImports: - - 'java.util.*' \ No newline at end of file + - 'java.util.*' diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index fa089f3..010bf12 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -25,7 +25,8 @@ jobs: key: ${{ runner.os }}-${{ hashFiles('**/build.gradle.kts') }} - name: Install dependencies run: | - sudo apt-get install libcurl4-openssl-dev + sudo apt update + sudo apt install libcurl4-openssl-dev - name: Build with Gradle run: ./gradlew commonBinaries - name: Move and apply correct permissions to binary @@ -66,4 +67,4 @@ jobs: file: ${{steps.download.outputs.download-path}} asset_name: slack-notifier-cli tag: ${{ github.ref }} - overwrite: true \ No newline at end of file + overwrite: true diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml new file mode 100644 index 0000000..c897ab3 --- /dev/null +++ b/.github/workflows/pull_request.yml @@ -0,0 +1,37 @@ +name: Pull Request Workflow + +on: [ pull_request ] + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Set up JDK + uses: actions/setup-java@v3 + with: + java-version: '17' + distribution: 'adopt' + cache: 'gradle' + - uses: actions/cache@v3 + with: + path: "/home/runner/.konan" + key: ${{ runner.os }}-${{ hashFiles('**/build.gradle.kts') }} + - name: Install dependencies + run: | + sudo apt update + sudo apt install libcurl4-openssl-dev + - name: Build with Gradle + run: ./gradlew commonBinaries + review-dog: + name: ReviewDog + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + # Detekt + - name: Run Detekt + uses: alaegin/Detekt-Action@v1.19 + with: + github_token: ${{ secrets.github_token }} + detekt_config: ./.github/detekt.yml diff --git a/.github/workflows/upgrade_gradle.yml b/.github/workflows/upgrade_gradle.yml new file mode 100644 index 0000000..57723f8 --- /dev/null +++ b/.github/workflows/upgrade_gradle.yml @@ -0,0 +1,14 @@ +name: Update Gradle Wrapper + +on: + schedule: + - cron: "0 7 * * *" + +jobs: + update-gradle-wrapper: + runs-on: ubuntu-latest + steps: + - name: Checkout Project + uses: actions/checkout@v3 + - name: Update Gradle Wrapper + uses: gradle-update/update-gradle-wrapper-action@v1 \ No newline at end of file diff --git a/README.md b/README.md index beeb025..665ede5 100644 --- a/README.md +++ b/README.md @@ -1 +1,9 @@ -### About \ No newline at end of file +### About + +A CLI project for sending slack messages usable in GitHub workflows. + +### Usage + +Typically you do not use this directly but via the [slack-notifier-cli-action](https://github.com/monta-app/slack-notifier-cli-action) + +Most project don't use this directly, but indirectly through the [github-workflows](https://github.com/monta-app/github-workflows) diff --git a/build.gradle.kts b/build.gradle.kts index e4accb5..209281e 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,16 +1,19 @@ plugins { - kotlin("multiplatform") version "1.8.10" - kotlin("plugin.serialization") version "1.8.10" - id("io.kotest.multiplatform") version "5.5.4" + kotlin("multiplatform") version "1.8.21" + kotlin("plugin.serialization") version "1.8.21" + id("io.kotest.multiplatform") version "5.6.2" + id("org.jlleitschuh.gradle.ktlint") version "11.3.2" } group = "com.monta.slack.notifier" -version = "1.0.9" +version = "1.1.0" repositories { mavenCentral() } +defaultTasks("commonBinaries") + kotlin { val hostOs = System.getProperty("os.name") @@ -40,11 +43,11 @@ kotlin { // Date Time Support implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.4.0") // Serialization - implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.0") + implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.1") // Atomic - implementation("org.jetbrains.kotlinx:atomicfu:0.20.0") + implementation("org.jetbrains.kotlinx:atomicfu:0.20.2") // Http Client - val ktorVersion = "2.2.4" + val ktorVersion = "2.3.0" implementation("io.ktor:ktor-client-core:$ktorVersion") implementation("io.ktor:ktor-client-curl:$ktorVersion") implementation("io.ktor:ktor-client-content-negotiation:$ktorVersion") @@ -53,7 +56,7 @@ kotlin { } val commonTest by getting { dependencies { - val kotestVersion = "5.5.4" + val kotestVersion = "5.6.2" implementation(kotlin("test-common")) implementation(kotlin("test-annotations-common")) implementation("io.kotest:kotest-framework-engine:$kotestVersion") @@ -67,4 +70,4 @@ kotlin.targets.withType) { PublishSlackCommand().main(args) -} \ No newline at end of file +} diff --git a/src/commonMain/kotlin/com/monta/slack/notifier/SlackClient.kt b/src/commonMain/kotlin/com/monta/slack/notifier/SlackClient.kt index 8b8c6c6..8d50644 100644 --- a/src/commonMain/kotlin/com/monta/slack/notifier/SlackClient.kt +++ b/src/commonMain/kotlin/com/monta/slack/notifier/SlackClient.kt @@ -1,34 +1,36 @@ package com.monta.slack.notifier -import com.monta.slack.notifier.model.* +import com.monta.slack.notifier.model.GithubPushContext +import com.monta.slack.notifier.model.JobStatus +import com.monta.slack.notifier.model.JobType +import com.monta.slack.notifier.model.SlackMessage import com.monta.slack.notifier.util.JsonUtil import com.monta.slack.notifier.util.client import io.ktor.client.request.* import io.ktor.client.statement.* import io.ktor.http.* +import io.ktor.utils.io.charsets.* import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable -import kotlinx.serialization.decodeFromString class SlackClient( private val serviceName: String?, private val serviceEmoji: String?, private val slackToken: String, - private val slackChannelId: String, + private val slackChannelId: String ) { suspend fun create( githubPushContext: GithubPushContext, jobType: JobType, - jobStatus: JobStatus, + jobStatus: JobStatus ): String { - val response = makeSlackRequest( url = "https://slack.com/api/chat.postMessage", message = generateMessage( githubPushContext = githubPushContext, jobType = jobType, - jobStatus = jobStatus, + jobStatus = jobStatus ) ) @@ -39,9 +41,8 @@ class SlackClient( messageId: String, githubPushContext: GithubPushContext, jobType: JobType, - jobStatus: JobStatus, + jobStatus: JobStatus ): String { - val previousMessage = getSlackMessageById(messageId) val response = makeSlackRequest( @@ -51,7 +52,7 @@ class SlackClient( jobType = jobType, jobStatus = jobStatus, messageId = messageId, - previousAttachments = previousMessage?.messages?.firstOrNull()?.attachments, + previousAttachments = previousMessage?.messages?.firstOrNull()?.attachments ) ) @@ -63,9 +64,8 @@ class SlackClient( jobType: JobType, jobStatus: JobStatus, messageId: String? = null, - previousAttachments: List? = null, + previousAttachments: List? = null ): SlackMessage { - val attachments = mutableMapOf() previousAttachments?.forEach { previousAttachment -> @@ -96,9 +96,8 @@ class SlackClient( } private suspend fun getSlackMessageById( - messageId: String, + messageId: String ): MessageResponse? { - val response = client.get { header("Authorization", "Bearer $slackToken") url { @@ -122,10 +121,9 @@ class SlackClient( } private suspend fun makeSlackRequest(url: String, message: SlackMessage): Response? { - val response = client.post(url) { header("Authorization", "Bearer $slackToken") - contentType(ContentType.Application.Json) + contentType(ContentType.Application.Json.withParameter("charset", Charsets.UTF_8.name)) setBody(message) } @@ -147,7 +145,7 @@ class SlackClient( @SerialName("channel") val channel: String, // C024BE91L @SerialName("ts") - val ts: String, // 1401383885.000061 + val ts: String // 1401383885.000061 ) @Serializable @@ -155,6 +153,6 @@ class SlackClient( @SerialName("ok") val ok: Boolean, // true @SerialName("messages") - val messages: List, + val messages: List ) -} \ No newline at end of file +} diff --git a/src/commonMain/kotlin/com/monta/slack/notifier/command/PublishSlackCommand.kt b/src/commonMain/kotlin/com/monta/slack/notifier/command/PublishSlackCommand.kt index db90857..3290253 100644 --- a/src/commonMain/kotlin/com/monta/slack/notifier/command/PublishSlackCommand.kt +++ b/src/commonMain/kotlin/com/monta/slack/notifier/command/PublishSlackCommand.kt @@ -15,7 +15,6 @@ class PublishSlackCommand : CliktCommand() { envvar = "PUBLISH_SLACK_GITHUB_CONTEXT" ).required() - private val serviceName: String? by option( help = "Emoji for the app!", envvar = "PUBLISH_SLACK_SERVICE_NAME" @@ -62,7 +61,7 @@ class PublishSlackCommand : CliktCommand() { githubContext = githubContext, jobType = JobType.fromString(jobType), jobStatus = JobStatus.fromString(jobStatus), - slackMessageId = slackMessageId, + slackMessageId = slackMessageId ) } } diff --git a/src/commonMain/kotlin/com/monta/slack/notifier/model/GithubPushContext.kt b/src/commonMain/kotlin/com/monta/slack/notifier/model/GithubPushContext.kt index 609ea49..af04628 100644 --- a/src/commonMain/kotlin/com/monta/slack/notifier/model/GithubPushContext.kt +++ b/src/commonMain/kotlin/com/monta/slack/notifier/model/GithubPushContext.kt @@ -1,6 +1,5 @@ package com.monta.slack.notifier.model - import com.monta.slack.notifier.util.buildTitle import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @@ -14,7 +13,7 @@ data class GithubPushContext( @SerialName("repository") val repository: String? = null, // monta-app/service-integrations @SerialName("run_id") - val runId: String? = null, // 4399287439 + val runId: String? = null, // 4399287439 @SerialName("actor") val actor: String? = null, // BrianEstrada @SerialName("triggering_actor") @@ -58,7 +57,7 @@ data class GithubPushContext( @SerialName("tree_id") val treeId: String? = null, // f3667a7332372de2ccb6a1cc5c310e780915a28e @SerialName("url") - val url: String? = null// https://github.com/monta-app/service-integrations/commit/c545a1613f18937a88a13935c4d644e8f81b71d6 + val url: String? = null // https://github.com/monta-app/service-integrations/commit/c545a1613f18937a88a13935c4d644e8f81b71d6 ) @Serializable @@ -68,7 +67,7 @@ data class GithubPushContext( @SerialName("name") val name: String? = null, // Brian Estrada @SerialName("username") - val username: String? = null// BrianEstrada + val username: String? = null // BrianEstrada ) { val displayName: String? = when { email == null && name == null -> null @@ -94,9 +93,8 @@ data class GithubPushContext( serviceEmoji: String?, slackChannelId: String, messageId: String?, - attachments: List?, + attachments: List? ): SlackMessage { - val commit = event?.headCommit val title = buildTitle(repository, workflow, serviceName, serviceEmoji) @@ -121,23 +119,23 @@ data class GithubPushContext( fields = listOf( SlackBlock.Text( type = "mrkdwn", - text = " \n*Branch:*\n${refName}", + text = " \n*Branch:*\n$refName" ), SlackBlock.Text( type = "mrkdwn", - text = " \n*Run:*\n<${getRunUrl()}|${runId}>", + text = " \n*Run:*\n<${getRunUrl()}|$runId>" ), SlackBlock.Text( type = "mrkdwn", - text = " \n*Comitter:*\n${commit?.committer?.displayName}", + text = " \n*Comitter:*\n${commit?.committer?.displayName}" ), SlackBlock.Text( type = "mrkdwn", - text = " \n*Message:*\n<${commit?.url}|${getSanitizedMessage()}>", + text = " \n*Message:*\n<${commit?.url}|${getSanitizedMessage()}>" ), SlackBlock.Text( type = "mrkdwn", - text = " \n*SHA:*\n<${commit?.url}|${commit?.id}>", + text = " \n*SHA:*\n<${commit?.url}|${commit?.id}>" ) ) ), @@ -148,4 +146,4 @@ data class GithubPushContext( attachments = attachments ) } -} \ No newline at end of file +} diff --git a/src/commonMain/kotlin/com/monta/slack/notifier/model/JobStatus.kt b/src/commonMain/kotlin/com/monta/slack/notifier/model/JobStatus.kt index ffa67f8..c9db180 100644 --- a/src/commonMain/kotlin/com/monta/slack/notifier/model/JobStatus.kt +++ b/src/commonMain/kotlin/com/monta/slack/notifier/model/JobStatus.kt @@ -2,7 +2,7 @@ package com.monta.slack.notifier.model enum class JobStatus( val message: String, - val color: String, + val color: String ) { Progress( message = "In Progress :construction:", @@ -32,4 +32,4 @@ enum class JobStatus( } ?: Unknown } } -} \ No newline at end of file +} diff --git a/src/commonMain/kotlin/com/monta/slack/notifier/model/JobType.kt b/src/commonMain/kotlin/com/monta/slack/notifier/model/JobType.kt index 82bdc5c..5f5e137 100644 --- a/src/commonMain/kotlin/com/monta/slack/notifier/model/JobType.kt +++ b/src/commonMain/kotlin/com/monta/slack/notifier/model/JobType.kt @@ -1,10 +1,10 @@ package com.monta.slack.notifier.model enum class JobType( - val label: String, + val label: String ) { Test( - label = "Test :test_tube:", + label = "Test :test_tube:" ), Build( label = "Build :building_construction:️" @@ -29,4 +29,4 @@ enum class JobType( } } } -} \ No newline at end of file +} diff --git a/src/commonMain/kotlin/com/monta/slack/notifier/model/SlackBlock.kt b/src/commonMain/kotlin/com/monta/slack/notifier/model/SlackBlock.kt index 4984c0f..9069ba0 100644 --- a/src/commonMain/kotlin/com/monta/slack/notifier/model/SlackBlock.kt +++ b/src/commonMain/kotlin/com/monta/slack/notifier/model/SlackBlock.kt @@ -10,7 +10,7 @@ class SlackBlock( @SerialName("text") val text: Text? = null, @SerialName("fields") - val fields: List? = null, + val fields: List? = null ) { @Serializable class Text( @@ -21,6 +21,6 @@ class SlackBlock( @SerialName("emoji") val emoji: Boolean = true, @SerialName("short") - val short: Boolean = true, + val short: Boolean = true ) -} \ No newline at end of file +} diff --git a/src/commonMain/kotlin/com/monta/slack/notifier/model/SlackMessage.kt b/src/commonMain/kotlin/com/monta/slack/notifier/model/SlackMessage.kt index be850ad..9aec1aa 100644 --- a/src/commonMain/kotlin/com/monta/slack/notifier/model/SlackMessage.kt +++ b/src/commonMain/kotlin/com/monta/slack/notifier/model/SlackMessage.kt @@ -9,7 +9,7 @@ data class SlackMessage( val ts: String? = null, val text: String? = null, val blocks: List? = null, - val attachments: List? = null, + val attachments: List? = null ) { @Serializable data class Attachment( @@ -40,7 +40,7 @@ data class SlackMessage( @SerialName("footer_icon") val footerIcon: String? = null, @SerialName("blocks") - val blocks: List? = null, + val blocks: List? = null ) { val jobType = JobType.fromLabel( @@ -54,7 +54,7 @@ data class SlackMessage( @SerialName("value") val value: String, // This field's value @SerialName("short") - val short: Boolean, // false + val short: Boolean // false ) @Serializable @@ -64,7 +64,7 @@ data class SlackMessage( @SerialName("value") val text: String, // This field's value @SerialName("url") - val url: String, // false + val url: String // false ) } -} \ No newline at end of file +} diff --git a/src/commonMain/kotlin/com/monta/slack/notifier/service/PublishSlackService.kt b/src/commonMain/kotlin/com/monta/slack/notifier/service/PublishSlackService.kt index a56ec21..ac725a9 100644 --- a/src/commonMain/kotlin/com/monta/slack/notifier/service/PublishSlackService.kt +++ b/src/commonMain/kotlin/com/monta/slack/notifier/service/PublishSlackService.kt @@ -6,13 +6,12 @@ import com.monta.slack.notifier.model.JobStatus import com.monta.slack.notifier.model.JobType import com.monta.slack.notifier.util.JsonUtil import com.monta.slack.notifier.util.writeToOutput -import kotlinx.serialization.decodeFromString class PublishSlackService( serviceName: String?, serviceEmoji: String?, slackToken: String, - slackChannelId: String, + slackChannelId: String ) { private val slackClient = SlackClient( @@ -26,23 +25,22 @@ class PublishSlackService( githubContext: String, jobType: JobType, jobStatus: JobStatus, - slackMessageId: String?, + slackMessageId: String? ): String { - val githubPushContext = JsonUtil.instance.decodeFromString(githubContext) val messageId = if (slackMessageId.isNullOrBlank()) { slackClient.create( githubPushContext = githubPushContext, jobType = jobType, - jobStatus = jobStatus, + jobStatus = jobStatus ) } else { slackClient.update( messageId = slackMessageId, githubPushContext = githubPushContext, jobType = jobType, - jobStatus = jobStatus, + jobStatus = jobStatus ) } diff --git a/src/commonMain/kotlin/com/monta/slack/notifier/util/GithubActionExtensions.kt b/src/commonMain/kotlin/com/monta/slack/notifier/util/GithubActionExtensions.kt index 1843357..578bebc 100644 --- a/src/commonMain/kotlin/com/monta/slack/notifier/util/GithubActionExtensions.kt +++ b/src/commonMain/kotlin/com/monta/slack/notifier/util/GithubActionExtensions.kt @@ -2,10 +2,13 @@ package com.monta.slack.notifier.util import kotlinx.cinterop.memScoped import kotlinx.cinterop.toKString -import platform.posix.* +import platform.posix.EOF +import platform.posix.fclose +import platform.posix.fopen +import platform.posix.fputs +import platform.posix.getenv fun writeToOutput(key: String, value: String) { - println("Writing to output $key $value") val githubOutput = getenv("GITHUB_OUTPUT")?.toKString() @@ -27,4 +30,4 @@ fun writeToOutput(key: String, value: String) { } finally { fclose(file) } -} \ No newline at end of file +} diff --git a/src/commonMain/kotlin/com/monta/slack/notifier/util/HttpClient.kt b/src/commonMain/kotlin/com/monta/slack/notifier/util/HttpClient.kt index 6698d76..842f81d 100644 --- a/src/commonMain/kotlin/com/monta/slack/notifier/util/HttpClient.kt +++ b/src/commonMain/kotlin/com/monta/slack/notifier/util/HttpClient.kt @@ -12,11 +12,13 @@ val client by lazy { HttpClient(Curl) { expectSuccess = false install(ContentNegotiation) { - json(Json { - explicitNulls = false - isLenient = true - ignoreUnknownKeys = true - }) + json( + Json { + explicitNulls = false + isLenient = true + ignoreUnknownKeys = true + } + ) } } -} \ No newline at end of file +} diff --git a/src/commonMain/kotlin/com/monta/slack/notifier/util/JsonUtil.kt b/src/commonMain/kotlin/com/monta/slack/notifier/util/JsonUtil.kt index 94b70f4..3359657 100644 --- a/src/commonMain/kotlin/com/monta/slack/notifier/util/JsonUtil.kt +++ b/src/commonMain/kotlin/com/monta/slack/notifier/util/JsonUtil.kt @@ -6,4 +6,4 @@ object JsonUtil { val instance = Json { ignoreUnknownKeys = true } -} \ No newline at end of file +} diff --git a/src/commonMain/kotlin/com/monta/slack/notifier/util/StringUtils.kt b/src/commonMain/kotlin/com/monta/slack/notifier/util/StringUtils.kt index 63e56d0..d827f48 100644 --- a/src/commonMain/kotlin/com/monta/slack/notifier/util/StringUtils.kt +++ b/src/commonMain/kotlin/com/monta/slack/notifier/util/StringUtils.kt @@ -1,19 +1,16 @@ package com.monta.slack.notifier.util - fun buildTitle( repository: String?, workflow: String?, serviceName: String?, serviceEmoji: String? ): String { - val title: String? = getTitle( serviceName = serviceName, repository = repository ) - return when { !title.isNullOrBlank() && !serviceEmoji.isNullOrBlank() -> { "$serviceEmoji $title - $workflow" @@ -53,4 +50,4 @@ private fun String?.toTitle(): String? { } } } -} \ No newline at end of file +}