Architecture¶
WorkflowKt is organized into three modules with clear separation of concerns:
workflowkt/
├── core/ # Runtime library
│ └── src/main/kotlin/ir/amirroid/workflowkt/
│ ├── GithubAction.kt # Core interface
│ ├── ActionResult.kt # Result types
│ ├── context/ # Typed event contexts
│ ├── environment/ # Environment variable access
│ ├── models/ # GitHub API data models
│ ├── core/ # Context factory & mappers
│ ├── config/ # JSON configuration
│ ├── serializer/ # Custom serializers
│ └── utils/ # Constants & extensions
├── gradle-plugin/ # Gradle plugin for task execution
│ └── src/main/kotlin/ir/amirroid/workflowkt/plugin/
│ ├── WorkflowKtPlugin.kt # Plugin entry point
│ ├── WorkflowKtExtension.kt # DSL configuration
│ ├── WorkflowKtRunTask.kt # Task execution logic
│ ├── exception/ # Custom exceptions
│ └── utils/ # Class name constants
└── sample/ # Example actions
Execution Flow¶
How a Workflow Runs¶
flowchart TD
A["./gradlew runWorkflowKt --key=..."] --> B[WorkflowKtRunTask.run]
B --> C{Test Mode?}
C -->|Yes| D[Load FakeEnvironment from workflowkt.properties]
C -->|No| E[Load GithubActionEnvironment from System.getenv]
D --> F[Create ActionContext via CompositeActionContextFactory]
E --> F
F --> G[Read GITHUB_EVENT_PATH JSON payload]
G --> H[Match eventName to ActionContextMapper]
H --> I[Map JSON to typed ActionContext]
I --> J[Invoke GithubAction.run]
J --> K{ActionResult}
K -->|Success| L[Write outputs to GITHUB_OUTPUT]
K -->|Failure| M[Throw WorkflowFailureException]
Key Components¶
-
GithubAction<C>— The core interface every workflow implements. Generic over the context typeC. -
ActionContextMapper<C>— Maps raw JSON event payloads to typedActionContextinstances. One mapper per GitHub event type. -
CompositeActionContextFactory— Delegates to the correct mapper based on the event name. -
GithubEnvironment— Composite interface providing typed access to all GitHub Actions environment variables. -
WorkflowKtRunTask— Gradle task that loads the action class via reflection, creates the environment and context, and invokes the action.
Class Loading¶
The Gradle plugin uses a URLClassLoader to load action classes at runtime. This provides:
- Classpath isolation — Actions can use their own dependencies without conflicts
- Reflection-based instantiation — Supports both classes with no-arg constructors and Kotlin
objectdeclarations - Generic type resolution — Determines the context type by inspecting the
GithubAction<C>type parameter
Event Matching¶
When not in test mode, the task checks whether the action's context type matches the current GitHub event:
- Inspects
GithubAction<C>to extract the concrete context typeC - Loads all
ActionContextMapperinstances - Finds the mapper whose context type matches
C - Gets the expected event name from that mapper
- Compares with
GITHUB_EVENT_NAMEenvironment variable - Skips execution if they don't match
Environment Layers¶
The GithubEnvironment interface composes four environment categories:
GithubEnvironment
├── GithubCoreEnvironments (GITHUB_ACTION, GITHUB_SHA, GITHUB_WORKFLOW, ...)
├── GithubRefEnvironments (GITHUB_REF, GITHUB_REF_NAME, GITHUB_HEAD_REF, ...)
├── GithubRunnerEnvironments (RUNNER_OS, RUNNER_ARCH, RUNNER_TEMP, ...)
└── GithubSystemEnvironments (CI, HOME, PATH)
In GitHub Actions, these read from System.getenv(). In test mode, a FakeEnvironment loads values from a properties file.
Data Model¶
All GitHub event payloads are modeled as Kotlin data classes with kotlinx.serialization:
classDiagram
class ActionContext {
<<sealed interface>>
+repository: Repository
}
class IssueContext {
+issue: Issue
+action: String
+sender: User
+changes: Changes
}
class PushContext {
+ref: String
+commits: List~Commit~
+headCommit: HeadCommit?
+pusher: Actor
}
class PullRequestContext {
+pullRequest: PullRequest
+action: String
+number: Int
}
class ReleaseContext {
+release: Release
+action: String
}
class ScheduleContext {
+schedule: String
+workflow: String
}
class WorkflowDispatchContext {
+inputs: Map~String,String~?
+ref: String
}
class CreateContext {
+ref: String
+refType: RefType
+pusherType: PusherType
}
ActionContext <|-- IssueContext
ActionContext <|-- PushContext
ActionContext <|-- PullRequestContext
ActionContext <|-- ReleaseContext
ActionContext <|-- ScheduleContext
ActionContext <|-- WorkflowDispatchContext
ActionContext <|-- CreateContext
Serialization¶
Timestamps from GitHub payloads are handled by a custom TimestampSerializer that supports both:
- ISO-8601 strings (e.g.,
"2025-12-23T21:38:14Z") - Unix epoch seconds (e.g.,
1734964694)
The JSON configuration uses ignoreUnknownKeys = true to be resilient to GitHub API changes.