From 0f46f4028b4af7eae478a27f01443789564a806c Mon Sep 17 00:00:00 2001 From: Daniel M Date: Wed, 14 Feb 2024 12:23:47 -0500 Subject: [PATCH] test --- .../com/dsoftware/ghmanager/Constants.kt | 8 - .../ghmanager/api/GetJobLogRequest.kt | 127 +++++++++++ .../ghmanager/api/GetRunLogRequest.kt | 80 ------- .../com/dsoftware/ghmanager/api/GithubApi.kt | 3 - .../dsoftware/ghmanager/api/model/JobModel.kt | 10 +- .../dsoftware/ghmanager/data/DataProviders.kt | 6 +- .../ghmanager/data/LogLoadingModelListener.kt | 33 +-- .../ghmanager/TestGetJobLogRequest.kt | 28 ++- src/test/resources/wf-run-jobs.json | 212 ++++-------------- 9 files changed, 202 insertions(+), 305 deletions(-) create mode 100644 src/main/kotlin/com/dsoftware/ghmanager/api/GetJobLogRequest.kt delete mode 100644 src/main/kotlin/com/dsoftware/ghmanager/api/GetRunLogRequest.kt diff --git a/src/main/kotlin/com/dsoftware/ghmanager/Constants.kt b/src/main/kotlin/com/dsoftware/ghmanager/Constants.kt index 857d5588..0ab2ab9c 100644 --- a/src/main/kotlin/com/dsoftware/ghmanager/Constants.kt +++ b/src/main/kotlin/com/dsoftware/ghmanager/Constants.kt @@ -9,14 +9,6 @@ object Constants { const val LOG_MSG_MISSING = "Job logs missing for: " const val LOG_MSG_JOB_IN_PROGRESS = "Job is still in progress, can't view logs." - fun emptyTextMessage(logValue: String): Boolean { - return (logValue.startsWith(LOG_MSG_MISSING) - || logValue.startsWith(LOG_MSG_PICK_JOB) - || logValue.startsWith( - LOG_MSG_JOB_IN_PROGRESS - )) - } - fun updateEmptyText(logValue: String, emptyText: StatusText): Boolean { if (logValue.startsWith(LOG_MSG_MISSING) || logValue.startsWith(LOG_MSG_PICK_JOB) diff --git a/src/main/kotlin/com/dsoftware/ghmanager/api/GetJobLogRequest.kt b/src/main/kotlin/com/dsoftware/ghmanager/api/GetJobLogRequest.kt new file mode 100644 index 00000000..bc8de9a5 --- /dev/null +++ b/src/main/kotlin/com/dsoftware/ghmanager/api/GetJobLogRequest.kt @@ -0,0 +1,127 @@ +package com.dsoftware.ghmanager.api + +import com.dsoftware.ghmanager.api.model.Job +import com.dsoftware.ghmanager.api.model.JobStep +import com.intellij.openapi.diagnostic.logger +import org.jetbrains.plugins.github.api.GithubApiRequest +import org.jetbrains.plugins.github.api.GithubApiResponse +import java.io.BufferedReader +import java.io.IOException +import java.io.InputStream +import java.io.InputStreamReader +import java.text.SimpleDateFormat +import java.util.Date +import java.util.TimeZone + +typealias JobLog = Map + +class GetJobLogRequest(private val job: Job) : GithubApiRequest.Get(job.url + "/logs") { + private val stepsPeriodMap = job.steps?.associate { step -> + step.number to (step.startedAt to step.completedAt) + } ?: emptyMap() + private val lastStepNumber: Int = stepsPeriodMap.keys.maxOrNull() ?: 0 + + override fun extractResult(response: GithubApiResponse): String { + LOG.debug("extracting result for $url") + return response.handleBody { + extractJobLogFromStream(it) + } + } + + fun extractJobLogFromStream(inputStream: InputStream): String { + val stepLogs = extractLogByStep(inputStream) + return stepsAsLog(stepLogs) + } + + fun extractLogByStep(inputStream: InputStream): Map { + val dateTimePattern = "yyyy-MM-dd'T'HH:mm:ss.SSS" + val formatter = SimpleDateFormat(dateTimePattern) + + val contentBuilders = HashMap() + + formatter.timeZone = TimeZone.getTimeZone("UTC") + var lineNum = 0 + var currStep = 1 + try { + val reader = BufferedReader(InputStreamReader(inputStream)) + val lines = reader.readLines() + for (line in lines) { + ++lineNum + if (line.length < 29) { + contentBuilders.getOrDefault(currStep, StringBuilder()).append(line + "\n") + continue + } + val datetimeStr = line.substring(0, 23) + val time = formatter.parse(datetimeStr) + currStep = findStep(currStep, time) + + contentBuilders.getOrPut(currStep) { StringBuilder(400_000) }.append(line + "\n") + } + } catch (e: IOException) { + LOG.warn(e.message) + throw e + } + return contentBuilders + } + + private fun findStep(initialStep: Int, time: Date): Int { + var currStep = initialStep + while (currStep < lastStepNumber) { + if (!stepsPeriodMap.containsKey(currStep)) { + currStep += 1 + continue + } + val currStart = stepsPeriodMap[currStep]?.first + val currEnd = stepsPeriodMap[currStep]?.second + if (currStart != null && currStart.after(time)) { + return currStep + } + if ((currStart == null || currStart.before(time) || currStart == time) + && (currEnd == null || currEnd.after(time) || currEnd == time) + ) { + return currStep + } + currStep += 1 + } + return currStep + } + + private fun stepsAsLog(stepLogs: JobLog): String { + val stepsResult: Map = if (job.steps == null) { + emptyMap() + } else { + job.steps.associateBy { it.number } + } + val stepNumbers = stepsResult.keys.sorted() + if (!stepNumbers.containsAll(stepLogs.keys)) { + LOG.warn( + "Some logs do not have a step-result associated " + + "[steps in results=$stepNumbers, step with logs=${stepLogs.keys}] " + ) + } + val res = StringBuilder(1_000_000) + for (index in stepNumbers) { + val stepInfo = stepsResult[index]!! + val indexStr = "%3d".format(index) + res.append( + when (stepInfo.conclusion) { + "skipped" -> "\u001B[0m\u001B[37m---- Step ${indexStr}: ${stepInfo.name} (skipped) ----\u001b[0m\n" + "failure" -> "\u001B[0m\u001B[31m---- Step ${indexStr}: ${stepInfo.name} (failed) ----\u001b[0m\n" + else -> "\u001B[0m\u001B[32m---- Step ${indexStr}: ${stepInfo.name} ----\u001b[0m\n" + } + ) + if (stepInfo.conclusion != "skipped" && stepLogs.containsKey(index) && (res.length < 950_000)) { + if (res.length + (stepLogs[index]?.length ?: 0) < 990_000) { + res.append(stepLogs[index]) + } else { + res.append("Log is too big to display, showing only first 1mb") + } + } + } + return res.toString() + } + + companion object { + private val LOG = logger() + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/dsoftware/ghmanager/api/GetRunLogRequest.kt b/src/main/kotlin/com/dsoftware/ghmanager/api/GetRunLogRequest.kt deleted file mode 100644 index c444be45..00000000 --- a/src/main/kotlin/com/dsoftware/ghmanager/api/GetRunLogRequest.kt +++ /dev/null @@ -1,80 +0,0 @@ -package com.dsoftware.ghmanager.api - -import com.dsoftware.ghmanager.api.model.Job -import com.intellij.openapi.diagnostic.logger -import com.jetbrains.rd.util.first -import org.jetbrains.plugins.github.api.GithubApiRequest -import org.jetbrains.plugins.github.api.GithubApiResponse -import java.io.BufferedReader -import java.io.IOException -import java.io.InputStream -import java.io.InputStreamReader -import java.text.SimpleDateFormat -import java.util.Date -import java.util.TimeZone - - -class GetJobLogRequest(private val job: Job) : GithubApiRequest.Get(job.url + "/logs") { - override fun extractResult(response: GithubApiResponse): JobLog { - LOG.debug("extracting result for $url") - return response.handleBody { - extractJobLogFromStream(it) - } - } - - fun extractJobLogFromStream(inputStream: InputStream): JobLog { - val dateTimePattern = "yyyy-MM-dd'T'HH:mm:ss.SSSSSSSX" - val formatter = SimpleDateFormat(dateTimePattern) - val stepsPeriodMap: Map> = job.steps?.associate { step -> - val start = if(step.startedAt != null) formatter.parse(step.startedAt) else null - val completed = if(step.completedAt != null) formatter.parse(step.completedAt) else null - step.number to (start to completed) - } ?: emptyMap() - val contentBuilders = HashMap() - - formatter.timeZone = TimeZone.getTimeZone("UTC") - fun findStep(datetimeStr: String): Int { - val time = formatter.parse(datetimeStr) - var lo = 0 - var hi = job.steps!!.size - 1 - while (lo <= hi) { - val mid = (lo + hi) / 2 - val midVal = stepsPeriodMap[mid] - if (midVal?.second != null && midVal.second!!.before(time)) { - lo = mid + 1 - } else if (midVal?.first != null && midVal.first!!.after(time)) { - hi = mid - 1 - } else { - return mid - } - } - return -1 - } - try { - val reader = BufferedReader(InputStreamReader(inputStream)) - val lines = reader.lines() - - var lineNum = 0 - var currStep = 0 - for (line in lines) { - ++lineNum - if (line.length < 29) { - contentBuilders.getOrDefault(currStep, StringBuilder()).append(line + "\n") - continue - } - val dateStr = line.substring(0, 28) - currStep = findStep(dateStr) - contentBuilders.getOrDefault(currStep, StringBuilder()).append(line + "\n") - } - } catch (e: IOException) { - LOG.warn(e.message) - throw e - } - return contentBuilders.map { (k, v) -> k to v.toString() }.toMap() - } - - companion object { - private val LOG = logger() - - } -} \ No newline at end of file diff --git a/src/main/kotlin/com/dsoftware/ghmanager/api/GithubApi.kt b/src/main/kotlin/com/dsoftware/ghmanager/api/GithubApi.kt index c1e1166a..f7f0197a 100644 --- a/src/main/kotlin/com/dsoftware/ghmanager/api/GithubApi.kt +++ b/src/main/kotlin/com/dsoftware/ghmanager/api/GithubApi.kt @@ -20,9 +20,6 @@ data class WorkflowRunFilter( val workflowId: Long? = null, ) -typealias JobLog = Map -typealias WorkflowRunLog = Map - object GithubApi : GithubApiRequests.Entity("/repos") { private val LOG = logger() fun getJobLog(job: Job) = GetJobLogRequest(job).withOperationName("Get Job log ${job.id}") diff --git a/src/main/kotlin/com/dsoftware/ghmanager/api/model/JobModel.kt b/src/main/kotlin/com/dsoftware/ghmanager/api/model/JobModel.kt index 9e032616..a1771993 100644 --- a/src/main/kotlin/com/dsoftware/ghmanager/api/model/JobModel.kt +++ b/src/main/kotlin/com/dsoftware/ghmanager/api/model/JobModel.kt @@ -35,6 +35,8 @@ data class Job( /* The id of the job. */ val id: Long, + val workflowName: String?, + val headBranch: String?, /* The id of the associated workflow run. */ val runId: Long, val runUrl: String, @@ -49,6 +51,8 @@ data class Job( val status: String, /* The outcome of the job. */ val conclusion: String?, + @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss'Z'", timezone = "UTC") + val createdAt: Date?, /* The time that the job started, in ISO 8601 format. */ @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss'Z'", timezone = "UTC") val startedAt: Date?, @@ -109,8 +113,10 @@ data class JobStep( /* The name of the job. */ val name: String, val number: Int, + @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSX", timezone = "UTC") /* The time that the step started, in ISO 8601 format. */ - val startedAt: String? = null, + val startedAt: Date? = null, /* The time that the job finished, in ISO 8601 format. */ - val completedAt: String? = null + @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSX", timezone = "UTC") + val completedAt: Date? = null ) \ No newline at end of file diff --git a/src/main/kotlin/com/dsoftware/ghmanager/data/DataProviders.kt b/src/main/kotlin/com/dsoftware/ghmanager/data/DataProviders.kt index 66961736..8fe85d8c 100644 --- a/src/main/kotlin/com/dsoftware/ghmanager/data/DataProviders.kt +++ b/src/main/kotlin/com/dsoftware/ghmanager/data/DataProviders.kt @@ -1,8 +1,6 @@ package com.dsoftware.ghmanager.data -import com.dsoftware.ghmanager.api.WorkflowRunLog import com.dsoftware.ghmanager.api.GithubApi -import com.dsoftware.ghmanager.api.JobLog import com.dsoftware.ghmanager.api.model.Job import com.dsoftware.ghmanager.api.model.WorkflowRunJobs import com.intellij.openapi.Disposable @@ -67,11 +65,11 @@ class JobLogDataProvider( progressManager: ProgressManager, requestExecutor: GithubApiRequestExecutor, job: Job -) : DataProvider( +) : DataProvider( progressManager, requestExecutor, GithubApi.getJobLog(job), - emptyMap() + null ) class WorkflowRunJobsDataProvider( diff --git a/src/main/kotlin/com/dsoftware/ghmanager/data/LogLoadingModelListener.kt b/src/main/kotlin/com/dsoftware/ghmanager/data/LogLoadingModelListener.kt index 235be01e..38bd5f7c 100644 --- a/src/main/kotlin/com/dsoftware/ghmanager/data/LogLoadingModelListener.kt +++ b/src/main/kotlin/com/dsoftware/ghmanager/data/LogLoadingModelListener.kt @@ -20,7 +20,7 @@ class LogLoadingModelListener( private val jobsSelectionHolder: JobListSelectionHolder, ) : GHLoadingModel.StateChangeListener { val logModel = SingleValueModel(null) - val logsLoadingModel = GHCompletableFutureLoadingModel(workflowRunDisposable) + val logsLoadingModel = GHCompletableFutureLoadingModel(workflowRunDisposable) init { jobsSelectionHolder.addSelectionChangeListener(workflowRunDisposable, this::setLogValue) @@ -50,34 +50,7 @@ class LogLoadingModelListener( } - private fun stepsAsLog(stepLogs: Map, selection: Job): String { - val stepsResult: Map = if (selection.steps == null) { - emptyMap() - } else { - selection.steps.associateBy { it.number } - } - val stepNumbers = stepsResult.keys.sorted() - if (!stepNumbers.containsAll(stepLogs.keys)) { - LOG.warn( - "Some logs do not have a step-result associated " + - "[steps in results=$stepNumbers, step with logs=${stepLogs.keys}] " - ) - } - val res = StringBuilder() - for (index in stepNumbers) { - val stepInfo = stepsResult[index]!! - val logs = if (stepLogs.containsKey(index)) stepLogs[index] else "" - val indexStr = "%3d".format(index) - res.append( - when (stepInfo.conclusion) { - "skipped" -> "\u001B[0m\u001B[37m---- Step ${indexStr}: ${stepInfo.name} (skipped) ----\u001b[0m\n" - "failure" -> "\u001B[0m\u001B[31m---- Step ${indexStr}: ${stepInfo.name} (failed) ----\u001b[0m\n${logs}" - else -> "\u001B[0m\u001B[32m---- Step ${indexStr}: ${stepInfo.name} ----\u001b[0m\n${logs}" - } - ) - } - return res.toString() - } + private fun setLogValue() { val jobSelection = jobsSelectionHolder.selection @@ -91,7 +64,7 @@ class LogLoadingModelListener( jobSelection == null -> LOG_MSG_PICK_JOB jobSelection.status == "in_progress" -> LOG_MSG_JOB_IN_PROGRESS logs == null -> LOG_MSG_MISSING + jobSelection.name - else -> stepsAsLog(logs, jobSelection) + else -> logs } } diff --git a/src/test/kotlin/com/dsoftware/ghmanager/TestGetJobLogRequest.kt b/src/test/kotlin/com/dsoftware/ghmanager/TestGetJobLogRequest.kt index 2c36866d..a4431d4a 100644 --- a/src/test/kotlin/com/dsoftware/ghmanager/TestGetJobLogRequest.kt +++ b/src/test/kotlin/com/dsoftware/ghmanager/TestGetJobLogRequest.kt @@ -1,17 +1,33 @@ package com.dsoftware.ghmanager -import com.dsoftware.ghmanager.api.model.Job +import com.dsoftware.ghmanager.api.GetJobLogRequest +import com.dsoftware.ghmanager.api.model.WorkflowRunJobs import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.databind.PropertyNamingStrategies import com.fasterxml.jackson.module.kotlin.readValue import com.fasterxml.jackson.module.kotlin.registerKotlinModule -import com.intellij.testFramework.fixtures.BasePlatformTestCase +import junit.framework.TestCase -class TestGetJobLogRequest : BasePlatformTestCase() { +class TestGetJobLogRequest : TestCase() { private val mapper = ObjectMapper().registerKotlinModule() + override fun setUp() { + super.setUp() + mapper.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE); + } + fun testGetJobLogRequest() { - val logContent = TestGetJobLogRequest::class.java.getResource("wf-run-single-job.log")?.readText() - val jobJson = TestGetJobLogRequest::class.java.getResource("wf-run-single-job.json")?.readText() - val obj: List = mapper.readValue(json) + // arrange + val logContent = TestGetJobLogRequest::class.java.getResource("/wf-run-single-job.log")!!.readText() + val wfJobsJson = TestGetJobLogRequest::class.java.getResource("/wf-run-jobs.json")!!.readText() + val wfJobs: WorkflowRunJobs = mapper.readValue(wfJobsJson) + val job = wfJobs.jobs.first() + + //act + val jobLog = GetJobLogRequest(job).extractLogByStep(logContent.byteInputStream()) + + //assert + val jobLogLinesCount = jobLog.map { it.key to it.value.split("\n").size }.toMap() + assertTrue(jobLogLinesCount == mapOf((1 to 33), (2 to 21), (3 to 834), (4 to 813), (6 to 5), (8 to 15))) } } \ No newline at end of file diff --git a/src/test/resources/wf-run-jobs.json b/src/test/resources/wf-run-jobs.json index 0567a727..65cc3516 100644 --- a/src/test/resources/wf-run-jobs.json +++ b/src/test/resources/wf-run-jobs.json @@ -1,227 +1,95 @@ { - "total_count": 2, + "total_count": 1, "jobs": [ { - "id": 21251882098, - "run_id": 7792954290, - "workflow_name": "Build", + "id": 21454796844, + "run_id": 7863783013, + "workflow_name": "Push on master", "head_branch": "master", - "run_url": "https://api.github.com/repos/cunla/ghactions-manager/actions/runs/7792954290", + "run_url": "https://api.github.com/repos/cunla/fakeredis-py/actions/runs/7863783013", "run_attempt": 1, - "node_id": "CR_kwDOHYewBs8AAAAE8rX8cg", - "head_sha": "198fcd73a1808fb5949e3a31c47b97a81bab9426", - "url": "https://api.github.com/repos/cunla/ghactions-manager/actions/jobs/21251882098", - "html_url": "https://github.com/cunla/ghactions-manager/actions/runs/7792954290/job/21251882098", + "node_id": "CR_kwDOHLrRQs8AAAAE_s44LA", + "head_sha": "a0576c489ba7cad8cad4ba7e14a7fe30ef9959a1", + "url": "https://api.github.com/repos/cunla/fakeredis-py/actions/jobs/21454796844", + "html_url": "https://github.com/cunla/fakeredis-py/actions/runs/7863783013/job/21454796844", "status": "completed", "conclusion": "success", - "created_at": "2024-02-06T00:47:32Z", - "started_at": "2024-02-06T00:47:39Z", - "completed_at": "2024-02-06T00:51:13Z", - "name": "Build", + "created_at": "2024-02-11T18:09:46Z", + "started_at": "2024-02-11T18:09:52Z", + "completed_at": "2024-02-11T18:11:49Z", + "name": "Analyze (python)", "steps": [ { "name": "Set up job", "status": "completed", "conclusion": "success", "number": 1, - "started_at": "2024-02-06T00:47:38.000Z", - "completed_at": "2024-02-06T00:47:40.000Z" + "started_at": "2024-02-11T18:09:51.000Z", + "completed_at": "2024-02-11T18:09:55.000Z" }, { - "name": "Maximize Build Space", + "name": "Checkout repository", "status": "completed", "conclusion": "success", "number": 2, - "started_at": "2024-02-06T00:47:40.000Z", - "completed_at": "2024-02-06T00:48:37.000Z" + "started_at": "2024-02-11T18:09:55.000Z", + "completed_at": "2024-02-11T18:09:56.000Z" }, { - "name": "Fetch Sources", + "name": "Initialize CodeQL", "status": "completed", "conclusion": "success", "number": 3, - "started_at": "2024-02-06T00:48:37.000Z", - "completed_at": "2024-02-06T00:48:37.000Z" + "started_at": "2024-02-11T18:09:56.000Z", + "completed_at": "2024-02-11T18:10:09.000Z" }, { - "name": "Gradle Wrapper Validation", + "name": "Perform CodeQL Analysis", "status": "completed", "conclusion": "success", "number": 4, - "started_at": "2024-02-06T00:48:38.000Z", - "completed_at": "2024-02-06T00:48:38.000Z" + "started_at": "2024-02-11T18:10:09.000Z", + "completed_at": "2024-02-11T18:11:46.000Z" }, { - "name": "Setup Java", - "status": "completed", - "conclusion": "success", - "number": 5, - "started_at": "2024-02-06T00:48:38.000Z", - "completed_at": "2024-02-06T00:49:08.000Z" - }, - { - "name": "Run Tests", + "name": "Post Perform CodeQL Analysis", "status": "completed", "conclusion": "success", "number": 6, - "started_at": "2024-02-06T00:49:08.000Z", - "completed_at": "2024-02-06T00:50:07.000Z" + "started_at": "2024-02-11T18:11:47.000Z", + "completed_at": "2024-02-11T18:11:47.000Z" }, { - "name": "Collect Tests Result", + "name": "Post Initialize CodeQL", "status": "completed", - "conclusion": "skipped", + "conclusion": "success", "number": 7, - "started_at": "2024-02-06T00:50:07.000Z", - "completed_at": "2024-02-06T00:50:07.000Z" + "started_at": "2024-02-11T18:11:47.000Z", + "completed_at": "2024-02-11T18:11:47.000Z" }, { - "name": "plugin verifier", + "name": "Post Checkout repository", "status": "completed", "conclusion": "success", "number": 8, - "started_at": "2024-02-06T00:50:07.000Z", - "completed_at": "2024-02-06T00:50:50.000Z" - }, - { - "name": "Export Properties", - "status": "completed", - "conclusion": "success", - "number": 9, - "started_at": "2024-02-06T00:50:51.000Z", - "completed_at": "2024-02-06T00:50:54.000Z" - }, - { - "name": "Setup Plugin Verifier IDEs Cache", - "status": "completed", - "conclusion": "success", - "number": 10, - "started_at": "2024-02-06T00:50:54.000Z", - "completed_at": "2024-02-06T00:50:54.000Z" - }, - { - "name": "Run Plugin Verification tasks", - "status": "completed", - "conclusion": "success", - "number": 11, - "started_at": "2024-02-06T00:50:55.000Z", - "completed_at": "2024-02-06T00:51:09.000Z" - }, - { - "name": "Collect Plugin Verifier Result", - "status": "completed", - "conclusion": "success", - "number": 12, - "started_at": "2024-02-06T00:51:09.000Z", - "completed_at": "2024-02-06T00:51:10.000Z" - }, - { - "name": "Prepare Plugin Artifact", - "status": "completed", - "conclusion": "success", - "number": 13, - "started_at": "2024-02-06T00:51:10.000Z", - "completed_at": "2024-02-06T00:51:10.000Z" - }, - { - "name": "Upload artifact", - "status": "completed", - "conclusion": "success", - "number": 14, - "started_at": "2024-02-06T00:51:10.000Z", - "completed_at": "2024-02-06T00:51:11.000Z" - }, - { - "name": "Post Setup Plugin Verifier IDEs Cache", - "status": "completed", - "conclusion": "success", - "number": 26, - "started_at": "2024-02-06T00:51:11.000Z", - "completed_at": "2024-02-06T00:51:11.000Z" - }, - { - "name": "Post Setup Java", - "status": "completed", - "conclusion": "success", - "number": 27, - "started_at": "2024-02-06T00:51:13.000Z", - "completed_at": "2024-02-06T00:51:13.000Z" - }, - { - "name": "Post Fetch Sources", - "status": "completed", - "conclusion": "success", - "number": 28, - "started_at": "2024-02-06T00:51:13.000Z", - "completed_at": "2024-02-06T00:51:13.000Z" - }, - { - "name": "Complete job", - "status": "completed", - "conclusion": "success", - "number": 29, - "started_at": "2024-02-06T00:51:11.000Z", - "completed_at": "2024-02-06T00:51:11.000Z" - } - ], - "check_run_url": "https://api.github.com/repos/cunla/ghactions-manager/check-runs/21251882098", - "labels": [ - "ubuntu-latest" - ], - "runner_id": 5, - "runner_name": "GitHub Actions 5", - "runner_group_id": 2, - "runner_group_name": "GitHub Actions" - }, - { - "id": 21251980476, - "run_id": 7792954290, - "workflow_name": "Build", - "head_branch": "master", - "run_url": "https://api.github.com/repos/cunla/ghactions-manager/actions/runs/7792954290", - "run_attempt": 1, - "node_id": "CR_kwDOHYewBs8AAAAE8rd8vA", - "head_sha": "198fcd73a1808fb5949e3a31c47b97a81bab9426", - "url": "https://api.github.com/repos/cunla/ghactions-manager/actions/jobs/21251980476", - "html_url": "https://github.com/cunla/ghactions-manager/actions/runs/7792954290/job/21251980476", - "status": "completed", - "conclusion": "success", - "created_at": "2024-02-06T00:51:15Z", - "started_at": "2024-02-06T00:51:21Z", - "completed_at": "2024-02-06T00:51:25Z", - "name": "Release Draft", - "steps": [ - { - "name": "Set up job", - "status": "completed", - "conclusion": "success", - "number": 1, - "started_at": "2024-02-06T00:51:20.000Z", - "completed_at": "2024-02-06T00:51:21.000Z" - }, - { - "name": "Run release-drafter/release-drafter@v5", - "status": "completed", - "conclusion": "success", - "number": 2, - "started_at": "2024-02-06T00:51:21.000Z", - "completed_at": "2024-02-06T00:51:24.000Z" + "started_at": "2024-02-11T18:11:49.000Z", + "completed_at": "2024-02-11T18:11:49.000Z" }, { "name": "Complete job", "status": "completed", "conclusion": "success", - "number": 3, - "started_at": "2024-02-06T00:51:24.000Z", - "completed_at": "2024-02-06T00:51:24.000Z" + "number": 9, + "started_at": "2024-02-11T18:11:47.000Z", + "completed_at": "2024-02-11T18:11:47.000Z" } ], - "check_run_url": "https://api.github.com/repos/cunla/ghactions-manager/check-runs/21251980476", + "check_run_url": "https://api.github.com/repos/cunla/fakeredis-py/check-runs/21454796844", "labels": [ "ubuntu-latest" ], - "runner_id": 13, - "runner_name": "GitHub Actions 13", + "runner_id": 9, + "runner_name": "GitHub Actions 9", "runner_group_id": 2, "runner_group_name": "GitHub Actions" }