예제
io.deplite:sdk-android로 실제 자주 쓰이는 흐름을 모았어요.
모든 예제는 의존성 추가가 끝난 상태를 가정해요.
External — 트리거 호출
가장 단순한 사용. 코루틴 스코프 안에서 한 줄로 호출해요.
val deplite = Deplite(apiToken = BuildConfig.DEPLITE_API_TOKEN)
lifecycleScope.launch {
try {
val job = deplite.triggers.run(
triggerId = TRIGGER_ID,
params = buildJsonObject { put("ref", "main") },
idempotencyKey = UUID.randomUUID().toString(),
)
Log.i("Deplite", "${job.jobId} ${job.status}")
} catch (e: DepliteApiException) {
Log.e("Deplite", "${e.statusCode}: ${e.body}")
}
}External — 파일 올리고 트리거
빌드 산출물(APK·tarball)을 올린 뒤, 파일 ID를 트리거 params에 실어 워크플로우에 전달하는 패턴이에요.
val uploaded = deplite.files.upload(
file = File(context.cacheDir, "app.apk"),
cleanupRule = CleanupRule.Ttl(3600),
contentType = "application/vnd.android.package-archive",
)
val job = deplite.triggers.run(
triggerId = TRIGGER_ID,
params = buildJsonObject {
put("apkFileId", uploaded.id)
put("ref", "main")
},
idempotencyKey = "release-${System.currentTimeMillis()}",
)External — Sync 트리거로 결과까지 한 번에
responseMode: sync로 만들어진 트리거를 호출하면 워크플로우 완료까지 기다리고 output까지 그대로 받아요.
val job = deplite.triggers.run(
triggerId = SYNC_TRIGGER_ID,
params = buildJsonObject { put("ref", "v1.2.3") },
)
when {
job.timedOut -> {
// responseTimeoutMs 초과. 워크플로우는 백그라운드에서 계속 실행
}
job.status == "success" -> {
val url = job.output?.jsonObject?.get("url")?.jsonPrimitive?.content
Log.i("Deplite", "Deployed: $url")
}
}Embedded — Foreground Service로 Agent 운영
키오스크·전시 단말처럼 일반 앱 안에 Agent를 심는 시나리오예요.
첫 실행에서 enroll, 결과를 안전 저장소에 보관, 이후 부팅에선 그 자격으로 SSE를 구독해요.
class AgentService : Service() {
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
startForeground(NOTIF_ID, buildNotification())
scope.launch {
val (identity, privateKey, _) = SecureStore.loadAgent()
val agent = DepliteAgent(identity, privateKey)
// 30초 heartbeat (기본값 일치)
launch {
while (isActive) {
runCatching { agent.heartbeat() }
delay(30_000)
}
}
// 메인 이벤트 루프
agent.events().collect { ev ->
when (ev) {
is AgentEvent.Deploy -> handleDeploy(agent, ev.payload)
AgentEvent.SyncWorkflows -> reportWorkflows(agent)
AgentEvent.Revoke -> stopSelf()
AgentEvent.Ping -> {}
is AgentEvent.Unknown -> Log.w("Agent", "unknown: ${ev.name}")
}
}
}
return START_STICKY
}
private suspend fun handleDeploy(agent: DepliteAgent, payload: DeployPayload) {
try {
agent.jobs.appendLogs(payload.jobId, listOf(
LogItem(seq = 0, stream = LogStream.System, level = LogLevel.Info, content = "started")
))
val output = executeWorkflow(payload)
agent.jobs.reportResult(payload.jobId, JobResult.success(output = output))
} catch (e: Exception) {
agent.jobs.reportResult(payload.jobId, JobResult.failed(exitCode = 1, errorMessage = e.message))
}
}
override fun onDestroy() { scope.cancel(); super.onDestroy() }
}SecureStore는 예시명이에요.
실제로는 privateKey(raw 32바이트)는 암호화 후 저장하는 것을 추천드려요.
Embedded — 워크플로우 인벤토리 동기화
SyncWorkflows 이벤트가 오면 현재 내가 실행 가능한 워크플로우 목록을 서버에 알려요.
실제 YAML 본문은 보내지 않고 이름과 메타만 보고해요.
private suspend fun reportWorkflows(agent: DepliteAgent) {
agent.workflows.report(listOf(
WorkflowReport(name = "deploy-prod", verboseSteps = listOf("build", "push"), secretsKeys = listOf("DB_URL")),
WorkflowReport(name = "healthcheck", verboseSteps = listOf("ping")),
))
}에러 처리 — 재시도 가능한 케이스만 백오프
suspend fun <T> runWithRetry(max: Int = 3, block: suspend () -> T): T {
repeat(max) { i ->
try {
return block()
} catch (e: DepliteAuthException) {
throw e // 재시도 무의미
} catch (e: DepliteApiException) {
if (e.statusCode < 500 && e.statusCode != 429) throw e
if (i == max - 1) throw e
delay((1 shl i) * 500L) // 0.5s, 1s, 2s
}
}
error("unreachable")
}
runWithRetry { deplite.triggers.run(triggerId, params = buildJsonObject {}) }관련 문서
최종 수정 일자: