Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PR description CI check #3363

Merged
merged 13 commits into from
Jul 23, 2024
19 changes: 11 additions & 8 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,19 @@ Please check if your pull request fulfills the following requirements:
- [ ] I confirm that I've run the code locally and everything works as expected.
- [ ] My PR includes only the necessary changes to fix the issue (i.e., no unnecessary files or lines of code are changed).
- [ ] 🎬 I've attached a **screen recording** of using the new code to the next paragraph (if applicable).

## Screen recording of interacting with your changes:
<!--💡 Tip: Drag & drop the video here. 💡-->

## What's changed?
Describe with a few bullets **what's new:**
<!--💡 Tip: After each more important point leave one line empty and show your changes in markdown table with screenshots or screen recordings replacing {media}. In the end, it should look like this: 💡-->
- I've fixed...

Before|After
---------|---------
---|---
{media}|{media}
{media}|{media}
- ...
- ...

## Risk factors
**What may go wrong if we merge your PR?**
- ...
Expand All @@ -28,12 +27,16 @@ Before|After
**In what cases won't your code work?**
- ...
- ...
## Does this PR close any GitHub issues?

## Does this PR close any GitHub issues? (do not delete)
- Closes #{ISSUE_NUMBER}

<!--❗For example: - Closes #123 ❗-->
<!--💡 Tip: Replace {ISSUE_NUMBER} with the number of Ivy Wallet ISSUE (https://github.com/Ivy-Apps/ivy-wallet/issues)(❗NOT PR❗) which this pull request fixes. If done correctly, you'll see the issue title linked on PR preview. 💡-->
<!--⚠️ If done correctly, you'll see the issue title linked on the PR preview. ⚠️-->
<!--💡 Tip: Multiple issues:
- Closes #{ISSUE_NUMBER_1}, closes #{ISSUE_NUMBER_2}, closes #{ISSUE_NUMBER_3}
💡-->
## Troubleshooting CI failures ❌
-->
<!-- If the PR doesn't close any GitHub issues, type "Closes N/A" to pass the CI check. -->

## Troubleshooting GitHub Actions (CI) failures ❌
Pull request checks failing? Read our [CI Troubleshooting guide](https://github.com/Ivy-Apps/ivy-wallet/blob/main/docs/CI-Troubleshooting.md).
2 changes: 1 addition & 1 deletion .github/workflows/apk.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:
distribution: 'adopt'
java-version: '18'

- name: Enable Gradle Wrapper caching (optmization)
- name: Enable Gradle Wrapper caching (optimization)
uses: actions/cache@v4
with:
path: |
Expand Down
5 changes: 4 additions & 1 deletion .github/workflows/ci_actions_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,7 @@ jobs:
run: ./gradlew :ci-actions:issue-create-comment:test

- name: Test the CI "compose_stability" action
run: ./gradlew :ci-actions:compose-stability:test
run: ./gradlew :ci-actions:compose-stability:test

- name: Test the CI "pr-description-check" action
run: ./gradlew :ci-actions:pr-description-check:test
34 changes: 34 additions & 0 deletions .github/workflows/pr_description.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
name: PR description check

on:
pull_request:

jobs:
pr-description-check:
runs-on: ubuntu-latest
steps:
- name: Checkout GIT
uses: actions/checkout@v4

- name: Setup Java
uses: actions/setup-java@v4
with:
distribution: 'adopt'
java-version: '18'

- name: Enable Gradle Wrapper caching (optimization)
uses: actions/cache@v4
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
restore-keys: |
${{ runner.os }}-gradle-

- name: Extract PR description (base 64)
id: extract_description
run: echo "PR_DESCRIPTION=$(jq -r '.pull_request.body' $GITHUB_EVENT_PATH | base64 | tr -d '\n')" >> $GITHUB_ENV

- name: Run PR Description Check
run: ./gradlew :ci-actions:pr-description-check:run --args="${{ env.PR_DESCRIPTION }}"
12 changes: 12 additions & 0 deletions ci-actions/pr-description-check/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
plugins {
id("ivy.script")
application
}

application {
mainClass = "ivy.automate.pr.MainKt"
}

dependencies {
implementation(projects.ciActions.base)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package ivy.automate.pr

import arrow.core.Either

class ClosesIssueAnalyzer : PRDescriptionAnalyzer {
override fun analyze(prDescription: String): Either<String, Unit> {
val cleanedDescription = prDescription.removeMarkdownComments()

// Regex pattern to find "Closes #NUMBER" or "Closes N/A"
val closesPattern = Regex("""(?i)closes\s+#\d+|closes\s+n/a""")
if (closesPattern.containsMatchIn(cleanedDescription)) {
return Either.Right(Unit)
}

return Either.Left(MissingClosesProblem)
}

private fun String.removeMarkdownComments(): String =
replace(Regex("<!--.*?-->", RegexOption.DOT_MATCHES_ALL), "").trim()

companion object {
val MissingClosesProblem = buildString {
append("[PROBLEM] Missing Closes GitHub Issue section\n")
append("This PR does not close any GitHub issues. ")
append("Add \"Closes #123\" where 123 is the issue number ")
append("or \"Closes N/A\" if doesn't close any issue.")
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package ivy.automate.pr

import arrow.core.raise.catch
import kotlinx.coroutines.runBlocking
import kotlin.io.encoding.Base64
import kotlin.io.encoding.ExperimentalEncodingApi

@OptIn(ExperimentalEncodingApi::class)
fun main(args: Array<String>): Unit = runBlocking {
require(args.size == 1) { "CI error: Missing PR description argument" }
val description = catch({ String(Base64.decode(args.first())) }) { e ->
throw IllegalArgumentException("CI error: Base 64 decoding failed! $e")
}
println("Analyzing PR description:")
println(description)
println("------")

val analyzers = listOf<PRDescriptionAnalyzer>(
ClosesIssueAnalyzer(),
)

val problems = analyzers.mapNotNull {
it.analyze(description).leftOrNull()
}
if (problems.isNotEmpty()) {
throw PRDescriptionError(
buildString {
append("\nWe found problems in your PR (Pull Request) description. ")
append("Please, follow our PR template:\n")
append("https://github.com/Ivy-Apps/ivy-wallet/blob/main/.github/PULL_REQUEST_TEMPLATE.md\n")
problems.forEach {
append(it)
append("\n\n")
}
}
)
}

println("All good! The PR description looks fine.")
}

class PRDescriptionError(msg: String) : Exception(msg)
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package ivy.automate.pr

import arrow.core.Either

interface PRDescriptionAnalyzer {
fun analyze(prDescription: String): Either<String, Unit>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
package ivy.automate.pr

import arrow.core.Either
import com.google.testing.junit.testparameterinjector.TestParameter
import com.google.testing.junit.testparameterinjector.TestParameterInjector
import io.kotest.matchers.shouldBe
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith

@RunWith(TestParameterInjector::class)
class ClosesIssueAnalyzerTest {

private lateinit var analyzer: PRDescriptionAnalyzer

@Before
fun setup() {
analyzer = ClosesIssueAnalyzer()
}

enum class PRDescTestCase(
val prDescription: String,
val expectedResult: Either<String, Unit>
) {
VALID_SHORT_1(
prDescription = """
## Pull request (PR) checklist
Please check if your pull request fulfills the following requirements:
<!--💡 Tip: Tick checkboxes like this: [x] 💡-->
- [x] I've read the [Contribution Guidelines](https://github.com/Ivy-Apps/ivy-wallet/blob/main/CONTRIBUTING.md) and my PR doesn't break the rules.
- [x] I've read and understand the [Developer Guidelines](https://github.com/Ivy-Apps/ivy-wallet/blob/main/docs/Guidelines.md).
- [x] I confirm that I've run the code locally and everything works as expected.
- [x] My PR includes only the necessary changes to fix the issue (i.e., no unnecessary files or lines of code are changed).
- [x] 🎬 I've attached a **screen recording** of using the new code to the next paragraph (if applicable).

## What's changed?
- The new name reflects the full-featured nature of the application
## Does this PR close any GitHub issues?
- Closes #3361
""".trimIndent(),
expectedResult = Either.Right(Unit),
),
INVALID_SHORT_1(
prDescription = """
## Pull request (PR) checklist
Please check if your pull request fulfills the following requirements:
<!--💡 Tip: Tick checkboxes like this: [x] 💡-->
- [x] I've read the [Contribution Guidelines](https://github.com/Ivy-Apps/ivy-wallet/blob/main/CONTRIBUTING.md) and my PR doesn't break the rules.
- [x] I've read and understand the [Developer Guidelines](https://github.com/Ivy-Apps/ivy-wallet/blob/main/docs/Guidelines.md).
- [x] I confirm that I've run the code locally and everything works as expected.
- [x] My PR includes only the necessary changes to fix the issue (i.e., no unnecessary files or lines of code are changed).
- [x] 🎬 I've attached a **screen recording** of using the new code to the next paragraph (if applicable).

## What's changed?
- The new name reflects the full-featured nature of the application
""".trimIndent(),
expectedResult = Either.Left(ClosesIssueAnalyzer.MissingClosesProblem),
),
INVALID_DEFAULT_TEMPLATE(
prDescription = """
## Pull request (PR) checklist
Please check if your pull request fulfills the following requirements:
<!--💡 Tip: Tick checkboxes like this: [x] 💡-->
- [ ] I've read the [Contribution Guidelines](https://github.com/Ivy-Apps/ivy-wallet/blob/main/CONTRIBUTING.md) and my PR doesn't break the rules.
- [ ] I've read and understand the [Developer Guidelines](https://github.com/Ivy-Apps/ivy-wallet/blob/main/docs/Guidelines.md).
- [ ] I confirm that I've run the code locally and everything works as expected.
- [ ] My PR includes only the necessary changes to fix the issue (i.e., no unnecessary files or lines of code are changed).
- [ ] 🎬 I've attached a **screen recording** of using the new code to the next paragraph (if applicable).
## Screen recording of interacting with your changes:
<!--💡 Tip: Drag & drop the video here. 💡-->

## What's changed?
Describe with a few bullets **what's new:**
<!--💡 Tip: After each more important point leave one line empty and show your changes in markdown table with screenshots or screen recordings replacing {media}. In the end, it should look like this: 💡-->
- I've fixed...

Before|After
---------|---------
{media}|{media}
{media}|{media}
- ...
- ...
## Risk factors
**What may go wrong if we merge your PR?**
- ...
- ...

**In what cases won't your code work?**
- ...
- ...
## Does this PR close any GitHub issues? (do not delete)
- Closes #{ISSUE_NUMBER}
<!--❗For example: - Closes #123 ❗-->
<!--💡 Tip: Replace {ISSUE_NUMBER} with the number of Ivy Wallet ISSUE (https://github.com/Ivy-Apps/ivy-wallet/issues)(❗NOT PR❗) which this pull request fixes. If done correctly, you'll see the issue title linked on PR preview. 💡-->
<!--💡 Tip: Multiple issues:
- Closes #{ISSUE_NUMBER_1}, closes #{ISSUE_NUMBER_2}, closes #{ISSUE_NUMBER_3}

If the PR doesn't close any GitHub issues, type "Closes N/A" to pass the CI check.
💡-->
## Troubleshooting CI failures ❌
Pull request checks failing? Read our [CI Troubleshooting guide](https://github.com/Ivy-Apps/ivy-wallet/blob/main/docs/CI-Troubleshooting.md).
""".trimIndent(),
expectedResult = Either.Left(ClosesIssueAnalyzer.MissingClosesProblem),
),
VALID_TEMPLATE_CLOSES_NUMBER(
prDescription = """
## Pull request (PR) checklist
Please check if your pull request fulfills the following requirements:
<!--💡 Tip: Tick checkboxes like this: [x] 💡-->
- [ ] I've read the [Contribution Guidelines](https://github.com/Ivy-Apps/ivy-wallet/blob/main/CONTRIBUTING.md) and my PR doesn't break the rules.
- [ ] I've read and understand the [Developer Guidelines](https://github.com/Ivy-Apps/ivy-wallet/blob/main/docs/Guidelines.md).
- [ ] I confirm that I've run the code locally and everything works as expected.
- [ ] My PR includes only the necessary changes to fix the issue (i.e., no unnecessary files or lines of code are changed).
- [ ] 🎬 I've attached a **screen recording** of using the new code to the next paragraph (if applicable).

## Screen recording of interacting with your changes:
<!--💡 Tip: Drag & drop the video here. 💡-->

## What's changed?
Describe with a few bullets **what's new:**
- I've fixed...
-

Before|After
---------|---------
{media}|{media}
{media}|{media}

## Risk factors
**What may go wrong if we merge your PR?**
- ...
- ...

**In what cases won't your code work?**
- ...
- ...

## Does this PR close any GitHub issues? (do not delete)
- Closes #123

<!--❗For example: - Closes #123 ❗-->
<!--💡 Tip: Replace {ISSUE_NUMBER} with the number of Ivy Wallet ISSUE (https://github.com/Ivy-Apps/ivy-wallet/issues)(❗NOT PR❗) which this pull request fixes. If done correctly, you'll see the issue title linked on PR preview. 💡-->
<!--💡 Tip: Multiple issues:
- Closes #{ISSUE_NUMBER_1}, closes #{ISSUE_NUMBER_2}, closes #{ISSUE_NUMBER_3}

If the PR doesn't close any GitHub issues, type "Closes N/A" to pass the CI check.
💡-->

## Troubleshooting GitHub Actions (CI) failures ❌
Pull request checks failing? Read our [CI Troubleshooting guide](https://github.com/Ivy-Apps/ivy-wallet/blob/main/docs/CI-Troubleshooting.md).
""".trimIndent(),
expectedResult = Either.Right(Unit)
),
VALID_CLOSES_NA(
prDescription = "Closes N/A",
expectedResult = Either.Right(Unit),
)
}

@Test
fun `validate PR description check`(
@TestParameter testCase: PRDescTestCase,
) {
// When
val result = analyzer.analyze(testCase.prDescription)

// Then
result shouldBe testCase.expectedResult
}
}
6 changes: 6 additions & 0 deletions docs/CI-Troubleshooting.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@

If you see any of the PR checks failing (❌) go to [Actions](https://github.com/Ivy-Apps/ivy-wallet/actions) and find it there. Or simply click "Details" next to the failed check and explore the logs to see why it has failed.

## PR description check

It means that you didn't follow our [official PR template](../.github/PULL_REQUEST_TEMPLATE.md).
Update your PR description with all necessary information. You can also check the exact error by
clicking "Details" on the failing (❌) check.

## Detekt
[Detekt](https://detekt.dev/) is a static code analyzer for Kotlin that we use to enforce code readability and good practices.

Expand Down
1 change: 1 addition & 0 deletions settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ include(":ci-actions:base")
include(":ci-actions:compose-stability")
include(":ci-actions:issue-assign")
include(":ci-actions:issue-create-comment")
include(":ci-actions:pr-description-check")
include(":screen:accounts")
include(":screen:attributions")
include(":screen:balance")
Expand Down