Skip to content

Instantly share code, notes, and snippets.

@kevinherron
Created January 4, 2026 22:07
Show Gist options
  • Select an option

  • Save kevinherron/d795b72fbfdf9121705d8e9c6e7bbc41 to your computer and use it in GitHub Desktop.

Select an option

Save kevinherron/d795b72fbfdf9121705d8e9c6e7bbc41 to your computer and use it in GitHub Desktop.
// Configuration for module deployment to Ignition gateway
val ignitionGatewayUrl: String by project.extra {
project.findProperty("ignitionGatewayUrl") as String? ?: "http://localhost:8088"
}
val ignitionApiToken: String by project.extra {
project.findProperty("ignitionApiToken") as String? ?: ""
}
val deployModule by tasks.registering {
group = "ignition"
description = "Uploads and installs the module to the Ignition gateway"
dependsOn(tasks.named("build"))
doLast {
val gatewayUrl =
project.findProperty("ignitionGatewayUrl") as String? ?: "http://localhost:8088"
val apiToken = project.findProperty("ignitionApiToken") as String? ?: ""
if (apiToken.isBlank()) {
throw GradleException("ignitionApiToken is not set. Configure it in gradle.properties or pass via -PignitionApiToken=<token>")
}
val moduleName = ignitionModule.fileName.get()
val signedModlFile = file("build/${moduleName}.modl")
val unsignedModlFile = file("build/${moduleName}.unsigned.modl")
val modlFile = when {
unsignedModlFile.exists() -> unsignedModlFile
signedModlFile.exists() -> signedModlFile
else -> throw GradleException(
"Module file not found. Looked for:\n" +
" - ${unsignedModlFile.absolutePath}\n" +
" - ${signedModlFile.absolutePath}\n" +
"Run 'build' task first."
)
}
val uploadUrl =
java.net.URI("${gatewayUrl}/data/api/v1/modules/upload?fileName=${modlFile.name}")
.toURL()
logger.lifecycle("Uploading module ${modlFile.name} to $gatewayUrl...")
val uploadConnection = uploadUrl.openConnection() as java.net.HttpURLConnection
uploadConnection.apply {
requestMethod = "POST"
doOutput = true
setRequestProperty("X-Ignition-API-Token", apiToken)
setRequestProperty("Content-Type", "application/octet-stream")
connectTimeout = 30_000
readTimeout = 60_000
}
modlFile.inputStream().use { input ->
uploadConnection.outputStream.use { output ->
input.copyTo(output)
}
}
val uploadResponseCode = uploadConnection.responseCode
if (uploadResponseCode != 200) {
val errorBody =
uploadConnection.errorStream?.bufferedReader()?.readText() ?: "No error details"
throw GradleException("Upload failed with status $uploadResponseCode: $errorBody")
}
val uploadResponse = uploadConnection.inputStream.bufferedReader().readText()
logger.lifecycle("Upload response: $uploadResponse")
// Parse moduleId from response
val moduleIdRegex = """"moduleId"\s*:\s*"([^"]+)"""".toRegex()
val moduleId = moduleIdRegex.find(uploadResponse)?.groupValues?.get(1)
?: throw GradleException("Failed to parse moduleId from upload response: $uploadResponse")
logger.lifecycle("Module uploaded successfully. Module ID: $moduleId")
val encodedModuleId = java.net.URLEncoder.encode(moduleId, "UTF-8")
// Accept certificate if present (ignore 409 = already accepted)
val certUrl = java.net.URI(
"${gatewayUrl}/data/api/v1/modules/certificate?moduleId=${encodedModuleId}"
).toURL()
val certConnection = certUrl.openConnection() as java.net.HttpURLConnection
certConnection.apply {
requestMethod = "POST"
setRequestProperty("X-Ignition-API-Token", apiToken)
connectTimeout = 30_000
readTimeout = 30_000
}
val certResponseCode = certConnection.responseCode
when (certResponseCode) {
200 -> logger.lifecycle("Certificate accepted.")
409 -> logger.lifecycle("Certificate already accepted.")
400 -> logger.lifecycle("No certificate to accept.")
else -> logger.warn("Certificate acceptance returned: $certResponseCode")
}
// Accept EULA if present (ignore 409 = already accepted)
val eulaUrl = java.net.URI(
"${gatewayUrl}/data/api/v1/modules/eula?moduleId=${encodedModuleId}"
).toURL()
val eulaConnection = eulaUrl.openConnection() as java.net.HttpURLConnection
eulaConnection.apply {
requestMethod = "POST"
setRequestProperty("X-Ignition-API-Token", apiToken)
connectTimeout = 30_000
readTimeout = 30_000
}
val eulaResponseCode = eulaConnection.responseCode
when (eulaResponseCode) {
200 -> logger.lifecycle("EULA accepted.")
409 -> logger.lifecycle("EULA already accepted.")
400 -> logger.lifecycle("No EULA to accept.")
else -> logger.warn("EULA acceptance returned: $eulaResponseCode")
}
logger.lifecycle("Installing module...")
// Install the module
val installUrl = java.net.URI(
"${gatewayUrl}/data/api/v1/modules/install?moduleId=${encodedModuleId}"
).toURL()
val installConnection = installUrl.openConnection() as java.net.HttpURLConnection
installConnection.apply {
requestMethod = "POST"
setRequestProperty("X-Ignition-API-Token", apiToken)
connectTimeout = 30_000
readTimeout = 60_000
}
val installResponseCode = installConnection.responseCode
if (installResponseCode != 200) {
val errorBody =
installConnection.errorStream?.bufferedReader()?.readText() ?: "No error details"
throw GradleException("Install failed with status $installResponseCode: $errorBody")
}
val installResponse = installConnection.inputStream.bufferedReader().readText()
logger.lifecycle("Install response: $installResponse")
logger.lifecycle("Module $moduleId installed successfully!")
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment