Skip to content

Commit 1691f87

Browse files
authored
Release 0.0.4.4 (#25)
* introduce JacocoCoverageRule (#6) * introduce JacocoCoverageRule * add JacocoCoverageRuleTest * support all nested objects * improve test coverage * add more tests * make issue levels take text (#8) * config bintray * convert the Rule to abstract class (#7) * convert the Rule to abstract class * fix push.gradle * auto release (#9) * auto release * add release task to circle ci * add ci and pullRequest to Rule (#10) * add ci and pullRequest to Rule * fix test * exclude some authors from protected files rule (#11) * jacoco total coverage (#12) * introduce Github-enterprise (#15) * Unify dependencies versions by applying buildSrc (#14) * Making unify dependencies versions feature by applying buildSrc mechanism and Gradle DSL then change all the build.gradle.kts files of all the modules we have to be use the new kotlin class that I made. Since four classes are made : 1. Dependencies : that contains all the dependencies we will use. 2. MainApp : that contains the common things of the whole app. 3. MainClass : that contains the main classes of each module. 4. Projects : that contains the name of projects/modules we have tell now. 5. Versions : that contains the versions that we will use in Dependencies class. * Modify Naming of buildSrc files - Projects file renamed to Modules to be more meaningful. - MainApp file renamed to Project to be more specific. * fix file url for enterprise (#18) * fix file url for enterprise * fix file path by using filePath instead of class path * add UTs * update comment (#19) * update comment * fix parsing * make use of another github token * release 0.0.4 * support teamcity (#21) * Create README.md * pull request expose more info (#23) * add custom rule post link * Update README.md * introduce debuggable flag (#24) * update version to 0.0.4.4
1 parent 376d296 commit 1691f87

File tree

24 files changed

+231
-50
lines changed

24 files changed

+231
-50
lines changed

README.md

+75
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
# Koshry
2+
3+
4+
## What's Koshry?
5+
**Koshry**:
6+
- is a dev tool, runs during your **CI Build** or on your **Local Machine Build**.
7+
- gives your team the ability to monitor **pull requests** and take automatic **actions** depend on your rules result.
8+
- create a report of the result of applying your rules on the pull request and post it back as a comment on the pull request.
9+
- is written in **Kotlin** and runs on JVM.
10+
- has some of the [ready rules](https://github.com/tarek360/koshry/wiki) for common use cases.
11+
- is **customizable**, you can [write your own **Custom Rule**](https://medium.com/@Tarek360/your-first-koshry-rule-c6648ac34ca2), everything is a **Rule** in Koshry.
12+
- works on any project whatever the programming language you use in your project
13+
14+
15+
## Run Koshry in 3 steps:
16+
17+
**1-** Add Koshry to your build process:
18+
19+
Gradle:
20+
```gradle
21+
implementation "io.github.tarek360.koshry:koshry:0.0.4"
22+
```
23+
24+
**2-** Invoke Koshry with one rule at least.
25+
26+
```kotlin
27+
val koshryConfig = koshry {
28+
rules {
29+
rule = rule1
30+
rule = rule2
31+
}
32+
}
33+
34+
Koshry.run(koshryConfig)
35+
```
36+
37+
**3-** Add one environment variable to your CI.
38+
39+
Add your git host (Github for example) token as an environment variable to allow Koshry post a comment and update the commit status.
40+
Use `KOSHRY_GIT_HOST_TOKEN` as a key of the environment variable.
41+
42+
43+
## Koshry Rules:
44+
Each Koshry **Rule** does some work then returns a **Report** (or not) as a result of that work.
45+
46+
While you can [create your own custom rule](https://medium.com/@Tarek360/your-first-koshry-rule-c6648ac34ca2), Koshry has some of the ready rules, you can make use of them directly.
47+
These rules are applied to the **git diff** of the **pull request**.
48+
- **LineRule**: Apply a **condition** to all of the **added** and **modified lines** in the pull request.
49+
- **FileRule**: Apply a **condition** to all of the **added** and **modified files** in the pull request.
50+
- **ProtectedFilesRule**: Protect a list of files to be changed by someone hasn't permission to make a change.
51+
52+
**Check the [Wiki](https://github.com/tarek360/koshry/wiki) to know How to write Koshry Rules**
53+
54+
55+
## Koshry works automatically with:
56+
- CI
57+
- [Travis CI](https://travis-ci.com)
58+
- [circle ci](https://circleci.com)
59+
- [TeamCity](https://www.jetbrains.com/teamcity)
60+
61+
- Git host:
62+
- Github
63+
64+
## Contribution
65+
- Feel free to fork and open a pull request.
66+
- Open an issue if you found a bug or you need help.
67+
68+
69+
## How does Kosrhy work?
70+
In a brief, when you call `Koshry.run(koshryConfig)`, Koshry applies all the rules you have set and it aggregates all of the reports returned from the rules to one report, then it posts that report as a comment on the pull request.
71+
72+
**Note**: Kosrhy applies all of the rules sequentially.
73+
74+
## I use Koshry in Koshry
75+
I use Koshry here in this repository to apply some rules like **JaCoCo Test Coverage** rule to set the minimum percentage of test coverage of PRs, check Koshry report on one of my PRs [here](https://github.com/tarek360/koshry/pull/18)

app/src/main/java/io/github/tarek360/app/App.kt

+1-3
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,7 @@ import io.github.tarek360.rules.core.Level.ERROR
1111
fun main(_args: Array<String>) {
1212

1313
val configuration = koshry {
14-
15-
baseSha = ""
14+
debuggable = true
1615

1716
rules {
1817
rule = protectedFileRule {
@@ -46,7 +45,6 @@ fun main(_args: Array<String>) {
4645
rule = jacocoCoverageRule {
4746
classCoverageThreshold = 79 //79%
4847
csvFilePath = "build/reports/jacoco/jacoco.csv"
49-
// htmlFilePath = "https://tarek360.github.io/koshry/build/reports/"
5048
}
5149
}
5250
}

core/src/main/java/io/github/tarek360/core/Kssert.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,4 @@ infix fun Any?.mustEqualAndNotNull(expected: Any) {
1515
}
1616

1717
infix fun Collection<Any>.mustHaveSize(expected: Int) = assertEquals("unexpected size", expected, this.size)
18-
18+
fun Collection<Any>.mustEmpty() = this.mustHaveSize(0)

core/src/main/java/io/github/tarek360/core/Logger.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package io.github.tarek360.core
22

3-
const val DEBUGGABLE = false
3+
var debuggableLogger = false
44

55
val logger: Logger = Logger()
66

@@ -15,7 +15,7 @@ class Logger {
1515
}
1616

1717
fun d(msg: () -> String?) {
18-
if (DEBUGGABLE) {
18+
if (debuggableLogger) {
1919
println("$ANSI_WHITE${msg()}$ANSI_RESET")
2020
}
2121
}

core/src/main/java/io/github/tarek360/core/cl/CommanderImpl.kt

+19-12
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,26 @@ import java.io.BufferedReader
55

66
class CommanderImpl : Commander {
77

8-
override fun executeCL(command: String): List<String> {
9-
return execute(command)
10-
}
8+
override fun executeCL(command: String): List<String> {
9+
return execute(command)
10+
}
1111

12-
override fun executeSh(filePath: String) {
12+
override fun executeSh(filePath: String) {
1313

14-
}
14+
}
1515

16-
private fun execute(command: String): List<String> {
17-
logger.d { "command executed: {$command}" }
18-
val proc = Runtime.getRuntime().exec(command)
19-
val lines = proc.inputStream.bufferedReader().use(BufferedReader::readLines)
20-
proc.waitFor()
21-
return lines
22-
}
16+
private fun execute(command: String): List<String> {
17+
logger.d { "COMMAND EXECUTED: {$command}" }
18+
val proc = Runtime.getRuntime().exec(command)
19+
val lines = proc.inputStream.bufferedReader().use(BufferedReader::readLines)
20+
proc.waitFor()
21+
if (lines.isEmpty()) {
22+
logger.e { "COMMAND {$command} HAS NO OUTPUT!" }
23+
} else {
24+
logger.d { "COMMAND OUTPUT LINES:" }
25+
lines.forEach { logger.d { "- $it" } }
26+
logger.d { "------------- OUTPUT END!\n" }
27+
}
28+
return lines
29+
}
2330
}
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,25 @@
11
package io.github.tarek360.githost
22

3-
interface GitHost {
3+
import io.github.tarek360.core.logger
4+
5+
abstract class GitHost {
6+
7+
init {
8+
logger.d { "GitHost name: ${this.javaClass.typeName}" }
9+
}
410

511
/**
612
*@return Comment URL
713
*/
8-
fun postComment(comment: Comment): String?
14+
abstract fun postComment(comment: Comment): String?
915

10-
fun updateComment(comment: Comment, commentId: Int): String?
16+
abstract fun updateComment(comment: Comment, commentId: Int): String?
1117

12-
fun postStatus(status: Status)
18+
abstract fun postStatus(status: Status)
1319

14-
fun pushFile(filePath: String, branchName: String, commitMsg: String)
20+
abstract fun pushFile(filePath: String, branchName: String, commitMsg: String)
1521

16-
fun getPullRequestInfo(): PullRequest?
22+
abstract fun getPullRequestInfo(): PullRequest?
1723

18-
fun getPullRequestComments(): List<GitHostComment>?
24+
abstract fun getPullRequestComments(): List<GitHostComment>?
1925
}
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
11
package io.github.tarek360.githost
22

3-
data class PullRequest(val headSha: String?, val baseSha: String?, val author:String?)
4-
3+
data class PullRequest constructor(val headSha: String?,
4+
val baseSha: String?,
5+
val title: String?,
6+
val body: String?,
7+
val author: String?,
8+
val labels: ArrayList<String>
9+
)

githost/src/main/java/io/github/tarek360/githost/UnknownGitHost.kt

+5-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@ package io.github.tarek360.githost
22

33
import io.github.tarek360.core.logger
44

5-
class UnknownGitHost : GitHost {
5+
class UnknownGitHost : GitHost() {
6+
7+
init {
8+
logger.e { "Unknown GitHostType" }
9+
}
610

711
override fun postComment(comment: Comment): String? {
812
logger.w { "Unknown GitHost: Koshry can't post the report" }

githost/src/main/java/io/github/tarek360/githost/github/GitHub.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import java.net.HttpURLConnection.HTTP_CREATED
1212

1313
var baseUrl = ""
1414

15-
class GitHub(private val gitHostInfo: GitHostInfo, isEnterprise: Boolean = false) : GitHost {
15+
class GitHub(private val gitHostInfo: GitHostInfo, isEnterprise: Boolean = false) : GitHost() {
1616

1717
init {
1818
// if it's not mocked web server

githost/src/main/java/io/github/tarek360/githost/github/PullRequestParser.kt

+38-1
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,19 @@ class PullRequestParser {
1212
const val KEY_SHA = "sha"
1313
const val KEY_USER = "user"
1414
const val KEY_LOGIN = "login"
15+
const val KEY_TITLE = "title"
16+
const val KEY_BODY = "body"
17+
const val KEY_LABELS = "labels"
18+
const val KEY_LABEL_NAME = "name"
1519
}
1620

1721
fun parse(json: String?): PullRequest? {
1822
var headSha: String? = null
1923
var baseSha: String? = null
24+
var title: String? = null
25+
var body: String? = null
2026
var author: String? = null
27+
val labels = arrayListOf<String>()
2128

2229
json?.run {
2330
try {
@@ -37,18 +44,48 @@ class PullRequestParser {
3744
}
3845
}
3946

47+
if (jsonObject.has(KEY_TITLE)) {
48+
title = jsonObject.getString(KEY_TITLE)
49+
}
50+
51+
if (jsonObject.has(KEY_BODY)) {
52+
body = jsonObject.getString(KEY_BODY)
53+
}
54+
4055
if (jsonObject.has(KEY_USER)) {
4156
val baseJsonObject = jsonObject.getJSONObject(KEY_USER)
4257
if (baseJsonObject.has(KEY_LOGIN)) {
4358
author = baseJsonObject.getString(KEY_LOGIN)
4459
}
4560
}
4661

62+
if (jsonObject.has(KEY_LABELS)) {
63+
val labelsJsonArray = jsonObject.getJSONArray(KEY_LABELS)
64+
labelsJsonArray.forEach {
65+
if(it is JSONObject){
66+
if (it.has(KEY_LABEL_NAME)) {
67+
val name = it.getString(KEY_LABEL_NAME)
68+
labels.add(name)
69+
}
70+
}
71+
72+
}
73+
}
74+
75+
4776
} catch (ex: JSONException) {
4877
return null
4978
}
5079
}
51-
return PullRequest(headSha = headSha, baseSha = baseSha, author = author)
80+
81+
return PullRequest(
82+
headSha = headSha,
83+
baseSha = baseSha,
84+
title = title,
85+
body = body,
86+
author = author,
87+
labels = labels
88+
)
5289
}
5390

5491
}

githost/src/main/java/io/github/tarek360/githost/network/OkHttp.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package io.github.tarek360.githost.network
22

3-
import io.github.tarek360.core.DEBUGGABLE
3+
import io.github.tarek360.core.debuggableLogger
44
import io.github.tarek360.core.logger
55
import okhttp3.OkHttpClient
66
import okhttp3.logging.HttpLoggingInterceptor
@@ -10,7 +10,7 @@ val okhttp: OkHttpClient by lazy {
1010
val logger = HttpLoggingInterceptor(HttpLoggingInterceptor.Logger { m -> logger.d { m } })
1111
logger.level = HttpLoggingInterceptor.Level.BASIC
1212
val okhttpBuilder = OkHttpClient.Builder()
13-
if(DEBUGGABLE){
13+
if(debuggableLogger){
1414
okhttpBuilder.addInterceptor(logger)
1515
}
1616
okhttpBuilder.build()

githost/src/test/kotlin/io/github/tarek360/githost/github/PullRequestParserTest.kt

+21-5
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
11
package io.github.tarek360.githost.github
22

3-
import io.github.tarek360.core.mustEqualAndNotNull
4-
import io.github.tarek360.core.mustNotNull
5-
import io.github.tarek360.core.mustNull
3+
import io.github.tarek360.core.*
64
import org.junit.Test
75

8-
class PullRequestParserTest{
6+
class PullRequestParserTest {
97

108
@Test
119
fun parse() {
1210
// Arrange
1311
val pullRequestParser = PullRequestParser()
1412

1513
val json = "{\n" +
14+
"\"title\": \"pr title\"," +
15+
"\"body\": \"pr description\","+
1616
" \"user\": {\n" +
1717
" \"login\": \"tarek360\"\n" +
1818
" },\n" +
@@ -21,7 +21,15 @@ class PullRequestParserTest{
2121
" },\n" +
2222
" \"base\": {\n" +
2323
" \"sha\": \"def\"\n" +
24+
" },\n" +
25+
"\"labels\": [\n" +
26+
" {\n" +
27+
" \"name\": \"bug\"\n" +
28+
" },\n" +
29+
" {\n" +
30+
" \"name\": \"fix\"\n" +
2431
" }\n" +
32+
"],"+
2533
"}"
2634

2735
// Act
@@ -33,11 +41,16 @@ class PullRequestParserTest{
3341
headSha mustEqualAndNotNull "abc"
3442
baseSha mustEqualAndNotNull "def"
3543
author mustEqualAndNotNull "tarek360"
44+
title mustEqualAndNotNull "pr title"
45+
body mustEqualAndNotNull "pr description"
46+
labels mustHaveSize 2
47+
labels[0] mustEqualAndNotNull "bug"
48+
labels[1] mustEqualAndNotNull "fix"
3649
}
3750
}
3851

3952
@Test
40-
fun parse_null_user_head_base() {
53+
fun parse_null() {
4154

4255
// Arrange
4356
val pullRequestParser = PullRequestParser()
@@ -53,6 +66,9 @@ class PullRequestParserTest{
5366
headSha.mustNull()
5467
baseSha.mustNull()
5568
author.mustNull()
69+
title.mustNull()
70+
body.mustNull()
71+
labels.mustEmpty()
5672
}
5773
}
5874

gradle.properties

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ org.gradle.jvmargs=-Xmx1536m
1212
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
1313
# org.gradle.parallel=true
1414
GROUP_ID=io.github.tarek360.koshry
15-
VERSION=0.0.4.3
15+
VERSION=0.0.4.4
1616
BUILD_NUMBER=0
1717
DESCRIPTION=Koshry for CI
1818
BINTRAY_REPO=Koshry

0 commit comments

Comments
 (0)