Skip to content

Creating an Action

This guide walks through implementing a GithubAction from scratch.

Step 1: Choose Your Event Type

Each action handles a specific GitHub event. Pick the context type that matches your use case:

Context Event Use Case
PushContext push Validate commits, update badges
PullRequestContext pull_request Run checks, post comments
IssueContext issues Auto-label, notify
ReleaseContext release Deploy, notify
WorkflowDispatchContext workflow_dispatch Manual triggers
ScheduleContext schedule Cron jobs
CreateContext create Branch/tag creation events

Step 2: Implement GithubAction

Create a class that implements GithubAction with your chosen context type:

package com.example

import ir.amirroid.workflowkt.ActionResult
import ir.amirroid.workflowkt.GithubAction
import ir.amirroid.workflowkt.context.PushContext
import ir.amirroid.workflowkt.environment.GithubEnvironment

class ValidateCommitsAction : GithubAction<PushContext> {

    override fun run(
        context: PushContext,
        environment: GithubEnvironment
    ): ActionResult {
        // Your logic here
        return ActionResult.Success()
    }
}

Step 3: Implement Your Logic

Use the typed context and environment to access event data:

override fun run(
    context: PushContext,
    environment: GithubEnvironment
): ActionResult {
    // Access push event data
    val branch = context.ref
    val commits = context.commits
    val sender = context.sender.login
    val repo = context.repository.fullName

    // Access environment variables
    val sha = environment.sha
    val token = environment["GITHUB_TOKEN"]

    // Validate commits
    val invalid = commits.filter { commit ->
        !commit.message.startsWith("feat:") &&
        !commit.message.startsWith("fix:") &&
        !commit.message.startsWith("chore:")
    }

    return if (invalid.isEmpty()) {
        ActionResult.Success()
    } else {
        ActionResult.Failure("Found ${invalid.size} commits without conventional format")
    }
}

Step 4: Return Results

Success with Outputs

return ActionResult.Success(
    mapOf(
        "VALIDATED" to "true",
        "COMMIT_COUNT" to commits.size.toString()
    )
)

Failure with Reason

return ActionResult.Failure(
    "Commit '${commit.id.take(7)}' has invalid message: '${commit.message}'"
)

Complete Example

package com.example

import ir.amirroid.workflowkt.ActionResult
import ir.amirroid.workflowkt.GithubAction
import ir.amirroid.workflowkt.context.IssueContext
import ir.amirroid.workflowkt.environment.GithubEnvironment

class AutoLabelAction : GithubAction<IssueContext> {

    override fun run(
        context: IssueContext,
        environment: GithubEnvironment
    ): ActionResult {
        // Only run on newly opened issues
        if (context.action != "opened") {
            return ActionResult.Success()
        }

        val issue = context.issue
        val text = "${issue.title} ${issue.body ?: ""}".lowercase()

        val labels = mutableListOf<String>()

        if (text.contains("bug") || text.contains("error")) {
            labels.add("bug")
        }
        if (text.contains("feature") || text.contains("enhancement")) {
            labels.add("enhancement")
        }
        if (labels.isEmpty()) {
            return ActionResult.Success()
        }

        println("Would add labels to issue #${issue.number}: $labels")

        return ActionResult.Success(
            mapOf("LABELS" to labels.joinToString(","))
        )
    }
}

Requirements

  • Class must have a no-arg constructor (or be a Kotlin object)
  • Must implement GithubAction<C> where C is a concrete ActionContext type
  • The run method receives both the typed context and the environment