|
| 1 | +package io.github.typesafegithub.workflows.updates |
| 2 | + |
| 3 | +import io.github.typesafegithub.workflows.domain.Workflow |
| 4 | +import io.github.typesafegithub.workflows.shared.internal.findGitRoot |
| 5 | +import io.github.typesafegithub.workflows.shared.internal.getGithubTokenOrNull |
| 6 | +import kotlinx.coroutines.flow.onEach |
| 7 | +import kotlinx.coroutines.flow.onEmpty |
| 8 | +import kotlinx.coroutines.flow.toList |
| 9 | +import kotlinx.coroutines.runBlocking |
| 10 | +import kotlin.io.path.absolute |
| 11 | +import kotlin.io.path.name |
| 12 | +import kotlin.io.path.pathString |
| 13 | +import kotlin.io.path.relativeTo |
| 14 | + |
| 15 | +/** |
| 16 | + * will report all available updates in the terminal output and the github step summary |
| 17 | + * looks up the github token from env `GITHUB_TOKEN` by default |
| 18 | + * when no github token is present, reporting will be skipped |
| 19 | + * |
| 20 | + * @param reportWhenTokenUnset enable to use github api without a token |
| 21 | + * @param githubToken if not set, will try to load from the environment variable `GITHUB_TOKEN` |
| 22 | + */ |
| 23 | +public fun Workflow.reportAvailableUpdates( |
| 24 | + reportWhenTokenUnset: Boolean = false, |
| 25 | + githubToken: String? = null, |
| 26 | +): Unit = |
| 27 | + runBlocking { |
| 28 | + if (System.getenv("GHWKT_RUN_STEP") == null) { |
| 29 | + reportAvailableUpdatesInternal( |
| 30 | + reportWhenTokenUnset = reportWhenTokenUnset, |
| 31 | + githubToken = githubToken, |
| 32 | + ) |
| 33 | + } |
| 34 | + } |
| 35 | + |
| 36 | +internal suspend fun Workflow.reportAvailableUpdatesInternal( |
| 37 | + reportWhenTokenUnset: Boolean = false, |
| 38 | + githubToken: String? = null, |
| 39 | + stepSummary: GithubStepSummary? = GithubStepSummary.fromEnv(), |
| 40 | +) { |
| 41 | + availableVersionsForEachAction( |
| 42 | + reportWhenTokenUnset = reportWhenTokenUnset, |
| 43 | + githubToken = githubToken ?: getGithubTokenOrNull(), |
| 44 | + ).onEach { regularActionVersions -> |
| 45 | + val usesString = |
| 46 | + with(regularActionVersions.action) { |
| 47 | + "$actionOwner/$actionName@$actionVersion" |
| 48 | + } |
| 49 | + |
| 50 | + val stepNames = regularActionVersions.steps.map { it.name ?: it.id } |
| 51 | + |
| 52 | + if (regularActionVersions.newerVersions.isEmpty()) { |
| 53 | + return@onEach |
| 54 | + } |
| 55 | + |
| 56 | + githubGroup("new version available for $usesString") { |
| 57 | + val (file, line) = findDependencyDeclaration(regularActionVersions.action) |
| 58 | + |
| 59 | + if (file != null && line != null) { |
| 60 | + githubNotice( |
| 61 | + message = "updates available for ${file.pathString}:$line", |
| 62 | + file = file.name, |
| 63 | + line = line, |
| 64 | + ) |
| 65 | + } |
| 66 | + |
| 67 | + if (stepSummary != null && file != null) { |
| 68 | + stepSummary.appendLine("## available updates for `$usesString`") |
| 69 | + stepSummary.appendLine("used by steps: ${stepNames.joinToString { "`$it`" }}") |
| 70 | + val githubRepo = System.getenv("GITHUB_REPOSITORY") ?: "\$GITHUB_REPOSITORY" |
| 71 | + val refName = System.getenv("GITHUB_REF_NAME") ?: "\$GITHUB_REF_NAME" |
| 72 | + val baseUrl = "https://github.com/$githubRepo/tree/$refName" |
| 73 | + val lineAnchor = line?.let { "#L$line" }.orEmpty() |
| 74 | + val gitRoot = file.findGitRoot() |
| 75 | + val relativeToRepositoryRoot = |
| 76 | + file.absolute().relativeTo(gitRoot.absolute()) |
| 77 | + .joinToString("/") |
| 78 | + stepSummary.appendLine( |
| 79 | + "\n[${file.name}$lineAnchor]($baseUrl/${relativeToRepositoryRoot}$lineAnchor)", |
| 80 | + ) |
| 81 | + } |
| 82 | + |
| 83 | + stepSummary?.appendLine("\n```kotlin") |
| 84 | + regularActionVersions.newerVersions.forEach { version -> |
| 85 | + val mavenCoordinates = regularActionVersions.action.mavenCoordinatesForAction(version) |
| 86 | + println(mavenCoordinates) |
| 87 | + stepSummary?.appendLine("@file:DependsOn(\"$mavenCoordinates\")") |
| 88 | + } |
| 89 | + stepSummary?.appendLine("```\n") |
| 90 | + } |
| 91 | + }.onEmpty { |
| 92 | + githubNotice( |
| 93 | + message = "action-version-checker found no actions or skipped running", |
| 94 | + ) |
| 95 | + } |
| 96 | + .toList() |
| 97 | + .also { regularActionVersions -> |
| 98 | + if (regularActionVersions.isNotEmpty()) { |
| 99 | + val hasOutdatedVersions = |
| 100 | + regularActionVersions |
| 101 | + .any { regularActionVersion -> |
| 102 | + regularActionVersion.newerVersions.isNotEmpty() |
| 103 | + } |
| 104 | + |
| 105 | + if (!hasOutdatedVersions) { |
| 106 | + githubNotice( |
| 107 | + message = "action-version-checker found no outdated actions", |
| 108 | + ) |
| 109 | + |
| 110 | + stepSummary?.appendLine("action-version-checker found no outdated actions") |
| 111 | + } |
| 112 | + } |
| 113 | + } |
| 114 | +} |
0 commit comments