Skip to content
This repository has been archived by the owner on Nov 5, 2024. It is now read-only.

Commit

Permalink
Fix automation (#2772)
Browse files Browse the repository at this point in the history
* Refactor and fix missing `.bind()`

* Fix tests

* Cover ActionExecutor unhappy paths

* WIP: Finalize ActionExecutor.kt

* Fix tests

* Improve the comment analyzer

* Fix Detekt

* Fix the "Issue Assign" workflow

* Fix tests
  • Loading branch information
ILIYANGERMANOV authored Oct 8, 2023
1 parent ef9f95e commit 1fec104
Show file tree
Hide file tree
Showing 11 changed files with 304 additions and 59 deletions.
4 changes: 1 addition & 3 deletions .github/workflows/issue_assign.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,4 @@ jobs:
${{ runner.os }}-gradle-
- name: Execute "Issue Assign" automation
run: ./gradlew :automations:issue-assign:run --args='issueNumber=${{ github.event.issue.number }} gitHubPAT=$GIT_HUB_PAT'
env:
GIT_HUB_PAT: ${{ secrets.IVYWALLET_BOT_GITHUB_PAT_2 }}
run: ./gradlew :automations:issue-assign:run --args='issueNumber=${{ github.event.issue.number }} gitHubPAT=${{ secrets.IVYWALLET_BOT_GITHUB_PAT_2 }}'
Original file line number Diff line number Diff line change
Expand Up @@ -8,32 +8,26 @@ import ivy.automate.base.github.model.GitHubLabel
import ivy.automate.base.github.model.GitHubPAT
import ivy.automate.base.github.model.GitHubUsername
import ivy.automate.base.github.model.NotBlankTrimmedString
import ivy.automate.base.ktor.KtorClientScope

interface GitHubService {
context(KtorClientScope)
suspend fun fetchIssue(
issueNumber: GitHubIssueNumber
): Either<Throwable, GitHubIssue>

context(KtorClientScope)
suspend fun fetchIssueLabels(
issueNumber: GitHubIssueNumber
): Either<Throwable, List<GitHubLabel>>

context(KtorClientScope)
suspend fun fetchIssueComments(
issueNumber: GitHubIssueNumber
): Either<Throwable, List<GitHubComment>>

context(KtorClientScope)
suspend fun commentIssue(
pat: GitHubPAT,
issueNumber: GitHubIssueNumber,
text: NotBlankTrimmedString
): Either<Throwable, Unit>

context(KtorClientScope)
suspend fun assignIssue(
pat: GitHubPAT,
issueNumber: GitHubIssueNumber,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package ivy.automate.base.github

import arrow.core.Either
import arrow.core.raise.either
import io.ktor.client.HttpClient
import io.ktor.client.call.body
import io.ktor.client.request.get
import io.ktor.client.request.header
Expand All @@ -23,15 +24,15 @@ import ivy.automate.base.github.model.GitHubPAT
import ivy.automate.base.github.model.GitHubUser
import ivy.automate.base.github.model.GitHubUsername
import ivy.automate.base.github.model.NotBlankTrimmedString
import ivy.automate.base.ktor.KtorClientScope
import kotlinx.serialization.Serializable

class GitHubServiceImpl : GitHubService {
class GitHubServiceImpl(
private val ktorClient: HttpClient,
) : GitHubService {
companion object {
private const val BASE_URL = "https://api.github.com/repos/Ivy-Apps/ivy-wallet"
}

context(KtorClientScope)
override suspend fun fetchIssue(
issueNumber: GitHubIssueNumber
): Either<Throwable, GitHubIssue> = catchIO {
Expand All @@ -41,7 +42,6 @@ class GitHubServiceImpl : GitHubService {
)
}

context(KtorClientScope)
override suspend fun fetchIssueLabels(
issueNumber: GitHubIssueNumber
): Either<Throwable, List<GitHubLabel>> = catchIO {
Expand All @@ -54,7 +54,6 @@ class GitHubServiceImpl : GitHubService {
}
}

context(KtorClientScope)
override suspend fun fetchIssueComments(
issueNumber: GitHubIssueNumber
): Either<Throwable, List<GitHubComment>> = catchIO {
Expand All @@ -68,7 +67,6 @@ class GitHubServiceImpl : GitHubService {
}
}

context(KtorClientScope)
override suspend fun commentIssue(
pat: GitHubPAT,
issueNumber: GitHubIssueNumber,
Expand All @@ -82,7 +80,6 @@ class GitHubServiceImpl : GitHubService {
response.requireSuccess()
}

context(KtorClientScope)
override suspend fun assignIssue(
pat: GitHubPAT,
issueNumber: GitHubIssueNumber,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,40 +1,42 @@
package ivy.automate.issue

import arrow.core.Either
import arrow.core.raise.Raise
import arrow.core.raise.either
import ivy.automate.base.github.GitHubService
import ivy.automate.base.github.model.GitHubUser
import ivy.automate.base.github.model.NotBlankTrimmedString
import ivy.automate.base.ktor.KtorClientScope

context(GitHubService, KtorClientScope)
context(GitHubService)
suspend fun Action.AlreadyTaken.execute(
args: Args
): Either<String, String> = either {
val commentText = buildString {
append("⚠️ Hey @${user.username.value},")
warn(user)
append(" this issue is already taken by @${assignee.username.value}.")
append(" **Do not start working on it!**")
val issuesUrl = "https://github.com/Ivy-Apps/ivy-wallet/issues"
append(" Please, [pick another one]($issuesUrl).")
append("\n**Do not start working on it!**")
append("\nPlease, [pick another one](${Constants.ISSUES_URL}).")
readContributingMd()
}
comment(args, commentText).bind()
comment(args, commentText)
commentText
}

context(GitHubService, KtorClientScope)
context(GitHubService)
suspend fun Action.NotApproved.execute(
args: Args
): Either<String, String> = either {
val commentText = buildString {
append("⚠️ Hey @${user.username.value}")
append(" this issue is not approved, yet.")
append(" @${Constants.IVY_ADMIN} must approve it first.")
warn(user)
append(" this issue is **not approved**, yet.")
append("\n@${Constants.IVY_ADMIN} must approve it first.")
readContributingMd()
}
comment(args, commentText).bind()
comment(args, commentText)
commentText
}

context(GitHubService, KtorClientScope)
context(GitHubService)
suspend fun Action.AssignIssue.execute(
args: Args
): Either<String, String> = either {
Expand All @@ -48,24 +50,34 @@ suspend fun Action.AssignIssue.execute(

val commentText = buildString {
append("Thank you for your interest @${user.username.value}! \uD83C\uDF89")
append(" Assigned to you. You can work on it now ✅")
append(" If you don't want to work on it now, please unassign yourself")
append(" so other contributors can take it.")
append("\nIssue #${issueNumber.value} is assigned to you. You can work on it! ✅")
append("\n\n_If you don't want to work on it now, please unassign yourself")
append(" so other contributors can take it._")
readContributingMd()
}
comment(args, commentText).bind()
comment(args, commentText)
commentText
}

context(GitHubService, KtorClientScope)
context(Raise<String>, GitHubService)
private suspend fun comment(
args: Args,
text: String
): Either<String, Unit> = either {
) {
commentIssue(
pat = args.pat,
issueNumber = args.issueNumber,
text = NotBlankTrimmedString(text)
).mapLeft {
"Failed to comment: $it"
}
}.bind()
}

private fun StringBuilder.warn(user: GitHubUser) {
append("⚠️ Hey @${user.username.value},")
}

private fun StringBuilder.readContributingMd() {
append("\n\n")
append("Also, make sure to read our [Contribution Guidelines](${Constants.CONTRIBUTING_URL}).")
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,17 @@ package ivy.automate.issue
import ivy.automate.base.github.model.GitHubComment
import ivy.automate.base.github.model.GitHubUser

val TakeIssueIntentionPhrases = listOf(
"im on it",
"want to contribute",
"work on this",
"contribute on this",
"assign to me",
"assign this issue to me",
"assign this to me",
"give it a try"
)

sealed interface CommentIntention {
data class TakeIssue(
val user: GitHubUser
Expand All @@ -15,12 +26,8 @@ fun analyzeCommentIntention(comment: GitHubComment): CommentIntention {
val commentText = comment.text.lowercase()
.replace("'", "")

fun keyphrase(phrase: String): Boolean {
return phrase in commentText
}

return when {
keyphrase("im on it")
TakeIssueIntentionPhrases.any { it in commentText }
-> CommentIntention.TakeIssue(comment.author)

else -> CommentIntention.Unknown
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,7 @@ object Constants {
const val ARG_GITHUB_PAT = "gitHubPAT"
const val ARG_ISSUE_NUMBER = "issueNumber"
const val IVY_BOT_USERNAME = "ivywallet"

const val ISSUES_URL = "https://github.com/Ivy-Apps/ivy-wallet/issues"
const val CONTRIBUTING_URL = "https://github.com/Ivy-Apps/ivy-wallet/blob/main/CONTRIBUTING.md"
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import arrow.core.raise.either
import ivy.automate.base.github.GitHubService
import ivy.automate.base.github.model.GitHubIssueNumber
import ivy.automate.base.github.model.GitHubUser
import ivy.automate.base.ktor.KtorClientScope

sealed interface Action {
val issueNumber: GitHubIssueNumber
Expand All @@ -31,7 +30,7 @@ sealed interface Action {
) : Action
}

context(GitHubService, KtorClientScope)
context(GitHubService)
suspend fun determineAction(args: Args): Either<String, Action> = either {
val issueNumber = args.issueNumber
val intention = checkCommentsForIntention(issueNumber).bind()
Expand All @@ -43,7 +42,7 @@ suspend fun determineAction(args: Args): Either<String, Action> = either {
}
}

context(GitHubService, KtorClientScope)
context(GitHubService)
private suspend fun CommentIntention.TakeIssue.toAction(
issueNumber: GitHubIssueNumber,
): Either<String, Action> = either {
Expand All @@ -60,7 +59,7 @@ private suspend fun CommentIntention.TakeIssue.toAction(
Action.AssignIssue(issueNumber, user)
}

context(GitHubService, KtorClientScope)
context(GitHubService)
private suspend fun checkCommentsForIntention(
issueNumber: GitHubIssueNumber
): Either<String, CommentIntention?> = either {
Expand All @@ -75,7 +74,7 @@ private suspend fun checkCommentsForIntention(
analyzeCommentIntention(lastComment)
}

context(GitHubService, KtorClientScope)
context(GitHubService)
private suspend fun checkIfIssueIsAssigned(
issueNumber: GitHubIssueNumber
): Either<String, GitHubUser?> = either {
Expand All @@ -86,7 +85,7 @@ private suspend fun checkIfIssueIsAssigned(
issueInfo.assignee
}

context(GitHubService, KtorClientScope)
context(GitHubService)
private suspend fun checkLabelsForApproved(
issueNumber: GitHubIssueNumber
): Either<String, Boolean> = either {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import arrow.core.raise.either
import ivy.automate.base.IvyError
import ivy.automate.base.github.GitHubService
import ivy.automate.base.github.GitHubServiceImpl
import ivy.automate.base.ktor.KtorClientScope
import ivy.automate.base.ktor.ktorClientScope
import kotlinx.coroutines.runBlocking

Expand All @@ -16,7 +15,9 @@ data class Context(
fun main(args: Array<String>): Unit = runBlocking {
ktorClientScope {
val context = Context(
gitHubService = GitHubServiceImpl(),
gitHubService = GitHubServiceImpl(
ktorClient = ktorClient,
),
)
with(context) {
val result = execute(args).fold(
Expand All @@ -28,7 +29,7 @@ fun main(args: Array<String>): Unit = runBlocking {
}
}

context(GitHubService, KtorClientScope)
context(GitHubService)
private suspend fun execute(argsArr: Array<String>): Either<String, String> = either {
val args = parseArgs(argsArr.toList()).bind()
when (val action = determineAction(args).bind()) {
Expand Down
Loading

0 comments on commit 1fec104

Please sign in to comment.