Skip to content

Sample Actions

Real-world examples from the sample/ module demonstrating common workflow patterns.


PrintHelloWorldGithubAction

Source: sample/src/main/kotlin/ir/amirroid/workflowkt/samples/PrintHelloWorldGithubAction.kt Key: print_helloworld Context: IssueContext

A minimal example that prints environment variables and context data.

class PrintHelloWorldGithubAction : GithubAction<IssueContext> {

    override fun run(context: IssueContext, environment: GithubEnvironment): ActionResult {
        println(environment.require("CUSTOM_PARAM"))
        println(environment.toMap())
        println(context.sender.login)

        return ActionResult.Success()
    }
}

ValidateCommitsAction

Source: sample/src/main/kotlin/ir/amirroid/workflowkt/samples/ValidateCommitsAction.kt Key: validate_commits Context: PushContext

Validates that all commits follow the conventional commit format.

class ValidateCommitsAction : GithubAction<PushContext> {

    private val conventionalCommitPattern =
        Regex("^(feat|fix|docs|style|refactor|test|chore)(\\(.+\\))?: .+")

    override fun run(context: PushContext, environment: GithubEnvironment): ActionResult {
        val invalidCommits = context.commits.filter { commit ->
            !conventionalCommitPattern.matches(commit.message.lines().first())
        }

        return if (invalidCommits.isEmpty()) {
            ActionResult.Success(mapOf(
                "MESSAGE" to "All commits follow conventional commit format",
                "TIME" to Instant.now().toString()
            ))
        } else {
            ActionResult.Failure("Invalid commit messages found")
        }
    }
}

Pattern: Iterates over context.commits, validates each message against a regex, returns success with outputs or failure.


AutoLabelIssueAction

Source: sample/src/main/kotlin/ir/amirroid/workflowkt/samples/AutoLabelIssueAction.kt Key: auto_label Context: IssueContext

Automatically labels issues based on their title and body content.

class AutoLabelIssueAction : GithubAction<IssueContext> {

    override fun run(context: IssueContext, environment: GithubEnvironment): ActionResult {
        if (context.action !in listOf("opened", "edited")) {
            return ActionResult.Success()
        }

        val combinedText = "${context.issue.title.lowercase()} ${context.issue.body?.lowercase() ?: ""}"
        val labelsToAdd = mutableSetOf<String>()

        when {
            combinedText.contains("bug") || combinedText.contains("error") -> labelsToAdd.add("bug")
            combinedText.contains("feature") || combinedText.contains("enhancement") -> labelsToAdd.add("enhancement")
            combinedText.contains("documentation") || combinedText.contains("docs") -> labelsToAdd.add("documentation")
            combinedText.contains("question") || combinedText.contains("how to") -> labelsToAdd.add("question")
        }

        if (combinedText.contains("urgent") || combinedText.contains("critical")) {
            labelsToAdd.add("priority: high")
        }

        // Post labels via GitHub API using OkHttp
        // ...

        return ActionResult.Success()
    }
}

Pattern: Filters by action type, analyzes text content, makes HTTP calls to the GitHub API.


CodeQualityCheckAction

Source: sample/src/main/kotlin/ir/amirroid/workflowkt/samples/CodeQualityCheckAction.kt Key: code_quality Context: PullRequestContext

Runs quality checks on pull requests and posts results as a comment.

class CodeQualityCheckAction : GithubAction<PullRequestContext> {

    override fun run(context: PullRequestContext, environment: GithubEnvironment): ActionResult {
        if (context.action !in listOf("opened", "synchronize", "reopened")) {
            return ActionResult.Success()
        }

        val pr = context.pullRequest
        val results = runQualityChecks(pr)

        // Post check results as GitHub PR comment
        val githubToken = environment["GITHUB_TOKEN"]
        if (githubToken != null) {
            postCheckResults(environment.repository!!, pr.number, results, githubToken)
        }

        return if (results.allPassed) {
            ActionResult.Success()
        } else {
            ActionResult.Failure("Some quality checks failed")
        }
    }
}

Pattern: Runs simulated checks (lint, tests, coverage), formats a Markdown report, posts via GitHub API.


RunTestsAndNotifyWithEmail

Source: sample/src/main/kotlin/ir/amirroid/workflowkt/samples/RunTestsAndNotifyWithEmail.kt Key: run_tests Context: WorkflowDispatchContext

Runs the test suite and sends an HTML email with detailed results.

class RunTestsAndNotifyWithEmail : GithubAction<WorkflowDispatchContext> {

    override fun run(
        context: WorkflowDispatchContext,
        environment: GithubEnvironment
    ): ActionResult {
        val testResult = runTests(environment)
        val html = buildEmailHtml(context, environment, testResult)

        sendEmail(
            apiUrl = environment.require("EMAIL_API_URL"),
            apiKey = environment.require("EMAIL_API_KEY"),
            to = environment.require("NOTIFY_EMAIL"),
            subject = if (testResult.success) "Tests Passed" else "Tests Failed",
            html = html
        )

        return if (testResult.success) {
            ActionResult.Success()
        } else {
            ActionResult.Failure("Tests failed. Notification sent.")
        }
    }
}

Pattern: Executes a subprocess (./gradlew test), parses XML test reports, builds HTML email, sends via HTTP.


NotifyOnReleaseAction

Source: sample/src/main/kotlin/ir/amirroid/workflowkt/samples/NotifyOnReleaseAction.kt Key: notify_release Context: ReleaseContext

Sends an HTML email notification when a release is published.

class NotifyOnReleaseAction : GithubAction<ReleaseContext> {

    override fun run(context: ReleaseContext, environment: GithubEnvironment): ActionResult {
        val subject = "New release published: ${context.release.tagName}"
        val html = buildEmailHtml(context)

        sendEmail(
            apiUrl = environment.require("EMAIL_API_URL"),
            apiKey = environment.require("EMAIL_API_KEY"),
            to = environment.require("NOTIFY_EMAIL"),
            subject = subject,
            html = html
        )

        return ActionResult.Success()
    }
}

Pattern: Accesses release data (tagName, body, assets), builds HTML email, sends via HTTP.


NotifyOnCreateAction

Source: sample/src/main/kotlin/ir/amirroid/workflowkt/samples/NotifyOnCreateAction.kt Key: notify_create Context: CreateContext

Sends an email notification when a branch or tag is created.

class NotifyOnCreateAction : GithubAction<CreateContext> {

    override fun run(context: CreateContext, environment: GithubEnvironment): ActionResult {
        val subject = "New ${context.refType.name.lowercase()} created"
        val html = buildEmailHtml(context)

        sendEmail(
            apiUrl = environment.get("EMAIL_API_URL") ?: return ActionResult.Failure("..."),
            apiKey = environment.get("EMAIL_API_KEY") ?: return ActionResult.Failure("..."),
            to = environment.get("NOTIFY_EMAIL") ?: return ActionResult.Failure("..."),
            subject = subject,
            html = html
        )

        return ActionResult.Success()
    }
}

Pattern: Reads refType (BRANCH/TAG), builds contextual email, sends via HTTP.


Common Patterns

Filtering by Action Type

Most actions check the action property to only run on specific sub-events:

if (context.action !in listOf("opened", "synchronize", "reopened")) {
    return ActionResult.Success()
}

Accessing Environment Variables

// Optional access (returns null if not set)
val token = environment["GITHUB_TOKEN"]

// Required access (throws if not set)
val token = environment.require("GITHUB_TOKEN")

// With default value
val retries = environment.getInt("MAX_RETRIES", default = 3)

HTTP Calls with OkHttp

Several sample actions use OkHttp for API calls:

private val client = OkHttpClient()
private val json = "application/json; charset=utf-8".toMediaTypeOrNull()

val body = """{"body": "$comment"}""".toRequestBody(json)

val request = Request.Builder()
    .url("https://api.github.com/repos/$repo/issues/$number/comments")
    .addHeader("Authorization", "Bearer $token")
    .addHeader("Accept", "application/vnd.github+json")
    .post(body)
    .build()

client.newCall(request).execute().use { response ->
    if (!response.isSuccessful) {
        throw Exception("GitHub API error: ${response.code}")
    }
}