├── .gitattributes ├── .github └── workflows │ ├── build.yml │ └── release.yaml ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── build.gradle.kts ├── connection.template.yml ├── devops-genius.yml ├── docs ├── .gitignore ├── CNAME ├── _config.yml ├── business │ └── business-review.md ├── ci │ └── ci.md ├── code-review │ ├── code-review.md │ ├── prompt-design.md │ └── research.md ├── index.md ├── issue │ └── issue.md ├── logo.svg └── usage.md ├── fixtures ├── example.json └── prompt.unit-mesh.yml ├── gradle.properties ├── gradle ├── libs.versions.toml └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── prompt-test.main.kts ├── settings.gradle.kts └── src ├── main ├── kotlin │ └── cc │ │ └── unitmesh │ │ └── genius │ │ ├── CiCdCommand.kt │ │ ├── CodeReviewCommand.kt │ │ ├── IssueCommand.kt │ │ ├── Main.kt │ │ ├── context │ │ ├── ActionOption.kt │ │ └── GeniusAction.kt │ │ ├── devops │ │ ├── Issue.kt │ │ ├── KanbanFactory.kt │ │ └── kanban │ │ │ ├── GitHubKanban.kt │ │ │ ├── GitlabKanban.kt │ │ │ └── Kanban.kt │ │ ├── domain │ │ ├── cicd │ │ │ ├── CiFileGenerator.kt │ │ │ └── GitHubActionPromptFactory.kt │ │ └── review │ │ │ ├── CodeReviewAction.kt │ │ │ ├── CodeReviewContext.kt │ │ │ ├── CodeReviewPromptFactory.kt │ │ │ └── ReviewOption.kt │ │ ├── project │ │ ├── BuildSystemType.java │ │ ├── BuildSystemTypeDescriptor.kt │ │ └── GeniusProject.kt │ │ └── prompt │ │ ├── PromptBuilder.kt │ │ ├── PromptFactory.kt │ │ └── PromptsLoader.kt └── resources │ ├── code-review │ └── simple-review.open-ai.vm │ ├── container │ └── docker │ │ └── docker-file.vm │ ├── logback.xml │ └── prompts │ └── cicd │ ├── github │ ├── common │ │ ├── github_actions_events.prompt │ │ └── github_actions_publish_docker.prompt │ ├── gradle │ │ ├── github_actions_build_gradle.prompt │ │ └── github_actions_publish_gradle.prompt │ └── maven │ │ ├── github_actions_build_maven.prompt │ │ └── github_actions_publish_maven.prompt │ └── jenkins │ └── gradle │ └── jenkins_build_gradle.prompt └── test └── kotlin └── cc └── unitmesh └── genius ├── devops ├── KanbanFactoryTest.kt └── kanban │ └── GitHubKanbanTest.kt ├── domain └── review │ └── CodeReviewPromptFactoryTest.kt └── project └── GeniusProjectTest.kt /.gitattributes: -------------------------------------------------------------------------------- 1 | # 2 | # https://help.github.com/articles/dealing-with-line-endings/ 3 | # 4 | # Linux start script should use lf 5 | /gradlew text eol=lf 6 | 7 | # These are Windows script files and should use crlf 8 | *.bat text eol=crlf 9 | 10 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: [ push ] 4 | 5 | jobs: 6 | build: 7 | strategy: 8 | matrix: 9 | os: [ macos-latest, ubuntu-latest, windows-latest ] 10 | runs-on: ${{ matrix.os }} 11 | 12 | steps: 13 | - uses: actions/checkout@v3 14 | with: 15 | fetch-depth: 10 16 | - uses: actions/setup-java@v3 17 | with: 18 | distribution: 'zulu' 19 | java-version: '17' 20 | 21 | - name: Setup Gradle 22 | uses: gradle/gradle-build-action@v2.8.0 23 | with: 24 | arguments: build 25 | 26 | - name: Execute Gradle build 27 | run: ./gradlew build 28 | 29 | - name: Execute Gradle Coverage 30 | run: ./gradlew check 31 | 32 | - name: Execute Gradle Coverage 33 | if: runner.os == 'macOS' 34 | run: bash <(curl -s https://codecov.io/bash) 35 | -------------------------------------------------------------------------------- /.github/workflows/release.yaml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | tags: 6 | - '*' 7 | 8 | jobs: 9 | publish: 10 | runs-on: ubuntu-latest 11 | strategy: 12 | matrix: 13 | assets: 14 | - build/libs/devops-genius-*.jar 15 | 16 | permissions: 17 | contents: write 18 | packages: write 19 | steps: 20 | - uses: actions/checkout@v3 21 | - uses: actions/setup-java@v3 22 | with: 23 | distribution: 'zulu' 24 | java-version: '17' 25 | cache: 'gradle' 26 | 27 | - name: Setup Gradle 28 | run: ./gradlew build --no-daemon -x test 29 | 30 | - name: Upload assets to release 31 | uses: svenstaro/upload-release-action@v2 32 | with: 33 | repo_token: ${{ secrets.GITHUB_TOKEN }} 34 | file: ${{ matrix.assets }} 35 | tag: ${{ github.ref }} 36 | overwrite: true 37 | file_glob: true 38 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | .gradle 3 | build/ 4 | !gradle/wrapper/gradle-wrapper.jar 5 | !**/src/main/**/build/ 6 | !**/src/test/**/build/ 7 | 8 | ### STS ### 9 | .apt_generated 10 | .classpath 11 | .factorypath 12 | .project 13 | .settings 14 | .springBeans 15 | .sts4-cache 16 | bin/ 17 | !**/src/main/**/bin/ 18 | !**/src/test/**/bin/ 19 | 20 | ### IntelliJ IDEA ### 21 | .idea 22 | *.iws 23 | *.iml 24 | *.ipr 25 | out/ 26 | !**/src/main/**/out/ 27 | !**/src/test/**/out/ 28 | 29 | ### NetBeans ### 30 | /nbproject/private/ 31 | /nbbuild/ 32 | /dist/ 33 | /nbdist/ 34 | /.nb-gradle/ 35 | 36 | ### VS Code ### 37 | .vscode/ 38 | .env 39 | build/ 40 | connection.yml 41 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # [](https://github.com/unit-mesh/devops-genius/compare/v0.1.0...v) (2023-10-19) 2 | 3 | 4 | 5 | # [0.1.0](https://github.com/unit-mesh/devops-genius/compare/d088ed8e0172b58594529a4a73ef04f6da5f3e7b...v0.1.0) (2023-10-19) 6 | 7 | 8 | ### Bug Fixes 9 | 10 | * **devops:** add more testcases for commit message [#8](https://github.com/unit-mesh/devops-genius/issues/8) ([0f63500](https://github.com/unit-mesh/devops-genius/commit/0f63500e0048203bf155d01dc4ab914dd1b1e831)) 11 | * **devops:** fix testing for commit message [#8](https://github.com/unit-mesh/devops-genius/issues/8) ([ed6aad0](https://github.com/unit-mesh/devops-genius/commit/ed6aad04d6c60c474627e7fc839c4c4b08aecd52)) 12 | * fix tests ([5ef300b](https://github.com/unit-mesh/devops-genius/commit/5ef300b6bb178dbea7f6a4603e4087e65e43b8d5)) 13 | * fix tets ([0a4a7fc](https://github.com/unit-mesh/devops-genius/commit/0a4a7fc093bb2a65e2655fd72dd6da2375060822)) 14 | * fix typos ([81ad5bb](https://github.com/unit-mesh/devops-genius/commit/81ad5bb7f7698e50d90971119b56dc26b26f9b09)) 15 | * try to handle for Windows ([a3a3e2f](https://github.com/unit-mesh/devops-genius/commit/a3a3e2f5892e9b639509a74b459ade84af20e5f7)) 16 | 17 | 18 | ### Features 19 | 20 | * add: for new line end of line handle ([d27187f](https://github.com/unit-mesh/devops-genius/commit/d27187f621e601e57ac715312c87a29338df5c01)) 21 | * **devops:** add explain prompt for testing [#8](https://github.com/unit-mesh/devops-genius/issues/8) ([057bc64](https://github.com/unit-mesh/devops-genius/commit/057bc648a36604ffa3dd288642ad8c98753ac3d5)) 22 | * **devops:** add filter for patterns [#8](https://github.com/unit-mesh/devops-genius/issues/8) ([5733afe](https://github.com/unit-mesh/devops-genius/commit/5733afeec7042bf1a6f27c0549bde9f69c36055e)) 23 | * **devops:** add simple line match for commit [#8](https://github.com/unit-mesh/devops-genius/issues/8) ([b40a354](https://github.com/unit-mesh/devops-genius/commit/b40a354c0fea5c1aa41c0d34e115a0444e91e302)) 24 | * **devops:** feat first version git log parser [#8](https://github.com/unit-mesh/devops-genius/issues/8) ([012cc3f](https://github.com/unit-mesh/devops-genius/commit/012cc3f519c09c55676fa3cebe8adeadebdbbb26)) 25 | * **devops:** init basic ci/cd scripts ([93c985b](https://github.com/unit-mesh/devops-genius/commit/93c985b27e2a4a723c92a051c1000a7b4ee66758)) 26 | * **devops:** init basic for debug [#8](https://github.com/unit-mesh/devops-genius/issues/8) ([3daa9a1](https://github.com/unit-mesh/devops-genius/commit/3daa9a17e0c54f823486f9fd39b1797ddc81edb3)) 27 | * **devops:** init basic system build ([a441157](https://github.com/unit-mesh/devops-genius/commit/a44115749485d783891b0443b2b1f724686b9ad1)) 28 | * **devops:** init basic workflow [#8](https://github.com/unit-mesh/devops-genius/issues/8) ([38272ba](https://github.com/unit-mesh/devops-genius/commit/38272ba8c5293c7c4507b276702a56a5f049a05e)) 29 | * **devops:** init featch issue logic for github [#8](https://github.com/unit-mesh/devops-genius/issues/8) ([78a49be](https://github.com/unit-mesh/devops-genius/commit/78a49be0549b43ea55003ea45b4a655dade4dd48)) 30 | * **devops:** init first review command [#8](https://github.com/unit-mesh/devops-genius/issues/8) ([b9f51cc](https://github.com/unit-mesh/devops-genius/commit/b9f51cc02daeaa524f5a15cefc3e4d3e76d004ca)) 31 | * **devops:** init for github and gitlab parser [#8](https://github.com/unit-mesh/devops-genius/issues/8) ([3abad42](https://github.com/unit-mesh/devops-genius/commit/3abad429131f2f4fcf4c094b42241d9cb0a4066f)) 32 | * **devops:** init issue parser [#8](https://github.com/unit-mesh/devops-genius/issues/8) ([fe8c709](https://github.com/unit-mesh/devops-genius/commit/fe8c709538b0290ff391d54436ec1098e84c9d4d)) 33 | * **devops:** init parser for generate patch [#8](https://github.com/unit-mesh/devops-genius/issues/8) ([a31780e](https://github.com/unit-mesh/devops-genius/commit/a31780e7a2da19bbb93fdb4667aff2bf3ebdbde5)) 34 | * **devops:** init project config [#8](https://github.com/unit-mesh/devops-genius/issues/8) ([9a2abea](https://github.com/unit-mesh/devops-genius/commit/9a2abea815a3a333c60ad732c9e779c76d7da3ff)) 35 | * **devops:** init project config for yaml [#8](https://github.com/unit-mesh/devops-genius/issues/8) ([3ff6e43](https://github.com/unit-mesh/devops-genius/commit/3ff6e4392fdc0ebde3570b6d71b5c8e0417005ab)) 36 | * **devops:** init prompt factory [#8](https://github.com/unit-mesh/devops-genius/issues/8) ([bd18659](https://github.com/unit-mesh/devops-genius/commit/bd18659f0814af614228bd161ed70f69a80c0df2)) 37 | * **devops:** init prompt testing scripts [#8](https://github.com/unit-mesh/devops-genius/issues/8) ([e55da1e](https://github.com/unit-mesh/devops-genius/commit/e55da1e04ba8dbfb6dd20b0006e536eae68b34ef)) 38 | * init basic commands ([be9efde](https://github.com/unit-mesh/devops-genius/commit/be9efde6d944d9fed25643b40cd0091c0b5fb58a)) 39 | * init basic genius command ([d088ed8](https://github.com/unit-mesh/devops-genius/commit/d088ed8e0172b58594529a4a73ef04f6da5f3e7b)) 40 | * init for docs ([6c1c0ea](https://github.com/unit-mesh/devops-genius/commit/6c1c0ea60c94e2353f4c2ec63abf22741f96fd6b)) 41 | * init ignore type [#8](https://github.com/unit-mesh/devops-genius/issues/8) ([5c8981a](https://github.com/unit-mesh/devops-genius/commit/5c8981ad8081d300e383086f673d7364a19fb3e6)) 42 | * **review:** add prompt generate for [#8](https://github.com/unit-mesh/devops-genius/issues/8) ([bffb31e](https://github.com/unit-mesh/devops-genius/commit/bffb31ea27d9b38d22f78b9d89ddd3ee98c8f9bb)) 43 | * **review:** make it works with llm [#8](https://github.com/unit-mesh/devops-genius/issues/8) ([5fcae4f](https://github.com/unit-mesh/devops-genius/commit/5fcae4faa08147c8813dc381eb3510ae39069cb1)) 44 | * update sample for scripts ([c481657](https://github.com/unit-mesh/devops-genius/commit/c481657088a45b5bdd5da6f468b637df7b0da1f4)) 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Mozilla Public License Version 2.0 2 | ================================== 3 | 4 | 1. Definitions 5 | -------------- 6 | 7 | 1.1. "Contributor" 8 | means each individual or legal entity that creates, contributes to 9 | the creation of, or owns Covered Software. 10 | 11 | 1.2. "Contributor Version" 12 | means the combination of the Contributions of others (if any) used 13 | by a Contributor and that particular Contributor's Contribution. 14 | 15 | 1.3. "Contribution" 16 | means Covered Software of a particular Contributor. 17 | 18 | 1.4. "Covered Software" 19 | means Source Code Form to which the initial Contributor has attached 20 | the notice in Exhibit A, the Executable Form of such Source Code 21 | Form, and Modifications of such Source Code Form, in each case 22 | including portions thereof. 23 | 24 | 1.5. "Incompatible With Secondary Licenses" 25 | means 26 | 27 | (a) that the initial Contributor has attached the notice described 28 | in Exhibit B to the Covered Software; or 29 | 30 | (b) that the Covered Software was made available under the terms of 31 | version 1.1 or earlier of the License, but not also under the 32 | terms of a Secondary License. 33 | 34 | 1.6. "Executable Form" 35 | means any form of the work other than Source Code Form. 36 | 37 | 1.7. "Larger Work" 38 | means a work that combines Covered Software with other material, in 39 | a separate file or files, that is not Covered Software. 40 | 41 | 1.8. "License" 42 | means this document. 43 | 44 | 1.9. "Licensable" 45 | means having the right to grant, to the maximum extent possible, 46 | whether at the time of the initial grant or subsequently, any and 47 | all of the rights conveyed by this License. 48 | 49 | 1.10. "Modifications" 50 | means any of the following: 51 | 52 | (a) any file in Source Code Form that results from an addition to, 53 | deletion from, or modification of the contents of Covered 54 | Software; or 55 | 56 | (b) any new file in Source Code Form that contains any Covered 57 | Software. 58 | 59 | 1.11. "Patent Claims" of a Contributor 60 | means any patent claim(s), including without limitation, method, 61 | process, and apparatus claims, in any patent Licensable by such 62 | Contributor that would be infringed, but for the grant of the 63 | License, by the making, using, selling, offering for sale, having 64 | made, import, or transfer of either its Contributions or its 65 | Contributor Version. 66 | 67 | 1.12. "Secondary License" 68 | means either the GNU General Public License, Version 2.0, the GNU 69 | Lesser General Public License, Version 2.1, the GNU Affero General 70 | Public License, Version 3.0, or any later versions of those 71 | licenses. 72 | 73 | 1.13. "Source Code Form" 74 | means the form of the work preferred for making modifications. 75 | 76 | 1.14. "You" (or "Your") 77 | means an individual or a legal entity exercising rights under this 78 | License. For legal entities, "You" includes any entity that 79 | controls, is controlled by, or is under common control with You. For 80 | purposes of this definition, "control" means (a) the power, direct 81 | or indirect, to cause the direction or management of such entity, 82 | whether by contract or otherwise, or (b) ownership of more than 83 | fifty percent (50%) of the outstanding shares or beneficial 84 | ownership of such entity. 85 | 86 | 2. License Grants and Conditions 87 | -------------------------------- 88 | 89 | 2.1. Grants 90 | 91 | Each Contributor hereby grants You a world-wide, royalty-free, 92 | non-exclusive license: 93 | 94 | (a) under intellectual property rights (other than patent or trademark) 95 | Licensable by such Contributor to use, reproduce, make available, 96 | modify, display, perform, distribute, and otherwise exploit its 97 | Contributions, either on an unmodified basis, with Modifications, or 98 | as part of a Larger Work; and 99 | 100 | (b) under Patent Claims of such Contributor to make, use, sell, offer 101 | for sale, have made, import, and otherwise transfer either its 102 | Contributions or its Contributor Version. 103 | 104 | 2.2. Effective Date 105 | 106 | The licenses granted in Section 2.1 with respect to any Contribution 107 | become effective for each Contribution on the date the Contributor first 108 | distributes such Contribution. 109 | 110 | 2.3. Limitations on Grant Scope 111 | 112 | The licenses granted in this Section 2 are the only rights granted under 113 | this License. No additional rights or licenses will be implied from the 114 | distribution or licensing of Covered Software under this License. 115 | Notwithstanding Section 2.1(b) above, no patent license is granted by a 116 | Contributor: 117 | 118 | (a) for any code that a Contributor has removed from Covered Software; 119 | or 120 | 121 | (b) for infringements caused by: (i) Your and any other third party's 122 | modifications of Covered Software, or (ii) the combination of its 123 | Contributions with other software (except as part of its Contributor 124 | Version); or 125 | 126 | (c) under Patent Claims infringed by Covered Software in the absence of 127 | its Contributions. 128 | 129 | This License does not grant any rights in the trademarks, service marks, 130 | or logos of any Contributor (except as may be necessary to comply with 131 | the notice requirements in Section 3.4). 132 | 133 | 2.4. Subsequent Licenses 134 | 135 | No Contributor makes additional grants as a result of Your choice to 136 | distribute the Covered Software under a subsequent version of this 137 | License (see Section 10.2) or under the terms of a Secondary License (if 138 | permitted under the terms of Section 3.3). 139 | 140 | 2.5. Representation 141 | 142 | Each Contributor represents that the Contributor believes its 143 | Contributions are its original creation(s) or it has sufficient rights 144 | to grant the rights to its Contributions conveyed by this License. 145 | 146 | 2.6. Fair Use 147 | 148 | This License is not intended to limit any rights You have under 149 | applicable copyright doctrines of fair use, fair dealing, or other 150 | equivalents. 151 | 152 | 2.7. Conditions 153 | 154 | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted 155 | in Section 2.1. 156 | 157 | 3. Responsibilities 158 | ------------------- 159 | 160 | 3.1. Distribution of Source Form 161 | 162 | All distribution of Covered Software in Source Code Form, including any 163 | Modifications that You create or to which You contribute, must be under 164 | the terms of this License. You must inform recipients that the Source 165 | Code Form of the Covered Software is governed by the terms of this 166 | License, and how they can obtain a copy of this License. You may not 167 | attempt to alter or restrict the recipients' rights in the Source Code 168 | Form. 169 | 170 | 3.2. Distribution of Executable Form 171 | 172 | If You distribute Covered Software in Executable Form then: 173 | 174 | (a) such Covered Software must also be made available in Source Code 175 | Form, as described in Section 3.1, and You must inform recipients of 176 | the Executable Form how they can obtain a copy of such Source Code 177 | Form by reasonable means in a timely manner, at a charge no more 178 | than the cost of distribution to the recipient; and 179 | 180 | (b) You may distribute such Executable Form under the terms of this 181 | License, or sublicense it under different terms, provided that the 182 | license for the Executable Form does not attempt to limit or alter 183 | the recipients' rights in the Source Code Form under this License. 184 | 185 | 3.3. Distribution of a Larger Work 186 | 187 | You may create and distribute a Larger Work under terms of Your choice, 188 | provided that You also comply with the requirements of this License for 189 | the Covered Software. If the Larger Work is a combination of Covered 190 | Software with a work governed by one or more Secondary Licenses, and the 191 | Covered Software is not Incompatible With Secondary Licenses, this 192 | License permits You to additionally distribute such Covered Software 193 | under the terms of such Secondary License(s), so that the recipient of 194 | the Larger Work may, at their option, further distribute the Covered 195 | Software under the terms of either this License or such Secondary 196 | License(s). 197 | 198 | 3.4. Notices 199 | 200 | You may not remove or alter the substance of any license notices 201 | (including copyright notices, patent notices, disclaimers of warranty, 202 | or limitations of liability) contained within the Source Code Form of 203 | the Covered Software, except that You may alter any license notices to 204 | the extent required to remedy known factual inaccuracies. 205 | 206 | 3.5. Application of Additional Terms 207 | 208 | You may choose to offer, and to charge a fee for, warranty, support, 209 | indemnity or liability obligations to one or more recipients of Covered 210 | Software. However, You may do so only on Your own behalf, and not on 211 | behalf of any Contributor. You must make it absolutely clear that any 212 | such warranty, support, indemnity, or liability obligation is offered by 213 | You alone, and You hereby agree to indemnify every Contributor for any 214 | liability incurred by such Contributor as a result of warranty, support, 215 | indemnity or liability terms You offer. You may include additional 216 | disclaimers of warranty and limitations of liability specific to any 217 | jurisdiction. 218 | 219 | 4. Inability to Comply Due to Statute or Regulation 220 | --------------------------------------------------- 221 | 222 | If it is impossible for You to comply with any of the terms of this 223 | License with respect to some or all of the Covered Software due to 224 | statute, judicial order, or regulation then You must: (a) comply with 225 | the terms of this License to the maximum extent possible; and (b) 226 | describe the limitations and the code they affect. Such description must 227 | be placed in a text file included with all distributions of the Covered 228 | Software under this License. Except to the extent prohibited by statute 229 | or regulation, such description must be sufficiently detailed for a 230 | recipient of ordinary skill to be able to understand it. 231 | 232 | 5. Termination 233 | -------------- 234 | 235 | 5.1. The rights granted under this License will terminate automatically 236 | if You fail to comply with any of its terms. However, if You become 237 | compliant, then the rights granted under this License from a particular 238 | Contributor are reinstated (a) provisionally, unless and until such 239 | Contributor explicitly and finally terminates Your grants, and (b) on an 240 | ongoing basis, if such Contributor fails to notify You of the 241 | non-compliance by some reasonable means prior to 60 days after You have 242 | come back into compliance. Moreover, Your grants from a particular 243 | Contributor are reinstated on an ongoing basis if such Contributor 244 | notifies You of the non-compliance by some reasonable means, this is the 245 | first time You have received notice of non-compliance with this License 246 | from such Contributor, and You become compliant prior to 30 days after 247 | Your receipt of the notice. 248 | 249 | 5.2. If You initiate litigation against any entity by asserting a patent 250 | infringement claim (excluding declaratory judgment actions, 251 | counter-claims, and cross-claims) alleging that a Contributor Version 252 | directly or indirectly infringes any patent, then the rights granted to 253 | You by any and all Contributors for the Covered Software under Section 254 | 2.1 of this License shall terminate. 255 | 256 | 5.3. In the event of termination under Sections 5.1 or 5.2 above, all 257 | end user license agreements (excluding distributors and resellers) which 258 | have been validly granted by You or Your distributors under this License 259 | prior to termination shall survive termination. 260 | 261 | ************************************************************************ 262 | * * 263 | * 6. Disclaimer of Warranty * 264 | * ------------------------- * 265 | * * 266 | * Covered Software is provided under this License on an "as is" * 267 | * basis, without warranty of any kind, either expressed, implied, or * 268 | * statutory, including, without limitation, warranties that the * 269 | * Covered Software is free of defects, merchantable, fit for a * 270 | * particular purpose or non-infringing. The entire risk as to the * 271 | * quality and performance of the Covered Software is with You. * 272 | * Should any Covered Software prove defective in any respect, You * 273 | * (not any Contributor) assume the cost of any necessary servicing, * 274 | * repair, or correction. This disclaimer of warranty constitutes an * 275 | * essential part of this License. No use of any Covered Software is * 276 | * authorized under this License except under this disclaimer. * 277 | * * 278 | ************************************************************************ 279 | 280 | ************************************************************************ 281 | * * 282 | * 7. Limitation of Liability * 283 | * -------------------------- * 284 | * * 285 | * Under no circumstances and under no legal theory, whether tort * 286 | * (including negligence), contract, or otherwise, shall any * 287 | * Contributor, or anyone who distributes Covered Software as * 288 | * permitted above, be liable to You for any direct, indirect, * 289 | * special, incidental, or consequential damages of any character * 290 | * including, without limitation, damages for lost profits, loss of * 291 | * goodwill, work stoppage, computer failure or malfunction, or any * 292 | * and all other commercial damages or losses, even if such party * 293 | * shall have been informed of the possibility of such damages. This * 294 | * limitation of liability shall not apply to liability for death or * 295 | * personal injury resulting from such party's negligence to the * 296 | * extent applicable law prohibits such limitation. Some * 297 | * jurisdictions do not allow the exclusion or limitation of * 298 | * incidental or consequential damages, so this exclusion and * 299 | * limitation may not apply to You. * 300 | * * 301 | ************************************************************************ 302 | 303 | 8. Litigation 304 | ------------- 305 | 306 | Any litigation relating to this License may be brought only in the 307 | courts of a jurisdiction where the defendant maintains its principal 308 | place of business and such litigation shall be governed by laws of that 309 | jurisdiction, without reference to its conflict-of-law provisions. 310 | Nothing in this Section shall prevent a party's ability to bring 311 | cross-claims or counter-claims. 312 | 313 | 9. Miscellaneous 314 | ---------------- 315 | 316 | This License represents the complete agreement concerning the subject 317 | matter hereof. If any provision of this License is held to be 318 | unenforceable, such provision shall be reformed only to the extent 319 | necessary to make it enforceable. Any law or regulation which provides 320 | that the language of a contract shall be construed against the drafter 321 | shall not be used to construe this License against a Contributor. 322 | 323 | 10. Versions of the License 324 | --------------------------- 325 | 326 | 10.1. New Versions 327 | 328 | Mozilla Foundation is the license steward. Except as provided in Section 329 | 10.3, no one other than the license steward has the right to modify or 330 | publish new versions of this License. Each version will be given a 331 | distinguishing version number. 332 | 333 | 10.2. Effect of New Versions 334 | 335 | You may distribute the Covered Software under the terms of the version 336 | of the License under which You originally received the Covered Software, 337 | or under the terms of any subsequent version published by the license 338 | steward. 339 | 340 | 10.3. Modified Versions 341 | 342 | If you create software not governed by this License, and you want to 343 | create a new license for such software, you may create and use a 344 | modified version of this License if you rename the license and remove 345 | any references to the name of the license steward (except to note that 346 | such modified license differs from this License). 347 | 348 | 10.4. Distributing Source Code Form that is Incompatible With Secondary 349 | Licenses 350 | 351 | If You choose to distribute Source Code Form that is Incompatible With 352 | Secondary Licenses under the terms of this version of the License, the 353 | notice described in Exhibit B of this License must be attached. 354 | 355 | Exhibit A - Source Code Form License Notice 356 | ------------------------------------------- 357 | 358 | This Source Code Form is subject to the terms of the Mozilla Public 359 | License, v. 2.0. If a copy of the MPL was not distributed with this 360 | file, You can obtain one at http://mozilla.org/MPL/2.0/. 361 | 362 | If it is not possible or desirable to put the notice in a particular 363 | file, then You may include the notice in a location (such as a LICENSE 364 | file in a relevant directory) where a recipient would be likely to look 365 | for such a notice. 366 | 367 | You may add additional accurate notices of copyright ownership. 368 | 369 | Exhibit B - "Incompatible With Secondary Licenses" Notice 370 | --------------------------------------------------------- 371 | 372 | This Source Code Form is "Incompatible With Secondary Licenses", as 373 | defined by the Mozilla Public License, v. 2.0. 374 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

DevOps Genius

2 |

3 | logo 4 |

5 |

6 | 7 | CI/CD 8 | 9 | 10 | Powered By 11 | 12 | 13 | GitHub release (with filter) 14 | 15 |

16 | 17 | [Read the docs →](https://devops.unitmesh.cc/) 18 | 19 | DevOpsGenius 旨在结合 LLM 重塑软件开发中的 DevOps 实践。将 LLM 视为团队的初级 “打杂工”,为团队提供各类辅助能力,以提高开发流程的效率和质量。 20 | 21 | - **自动 CI/CD 脚本生成**:DevOpsGenius 能够自动分析代码仓库,生成对应的 CI/CD 脚本,以实现自动化的构建、测试和部署流程。 22 | - **自动代码评审**:DevOpsGenius 使用先进的代码分析和静态分析技术,自动检测潜在的问题、错误和不规范的代码风格,并提供有针对性的建议和改进意见。 23 | - **自动需求优化**:DevOpsGenius 能够自动分析需求,识别需求中的问题和不足,并提供有针对性的建议和改进意见。 24 | - **智能拉取请求处理**:DevOpsGenius 能够智能地审查和处理拉取请求。它自动识别代码变更、冲突和合并请求,并以自动化的方式进行验证、测试和部署流程,以确保高质量的代码交付。 25 | - **自动 bug 分析与修复建议**:DevOpsGenius 能够自动分析 bug,识别 bug 中的问题和不足,并提供有针对性的建议和改进意见。 26 | - **智能报告和统计**:DevOpsGenius生成详细的报告和统计数据,展示代码质量、团队绩效和项目进度等关键指标。这些洞察力有助于团队进行数据驱动的决策,优化开发流程和资源分配。 27 | - **SRE 智能化**:DevOpsGenius 能够自动分析系统运行状态,识别系统中的问题和不足,并提供有针对性的建议和改进意见。 28 | - **容器化配置生成**:DevOpsGenius 能够自动分析代码仓库,生成对应的容器化配置文件,以实现自动化的容器化流程。 29 | 30 | ## License 31 | 32 | This code is distributed under the MPL 2.0 license. See `LICENSE` in this directory. 33 | -------------------------------------------------------------------------------- /build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | java 3 | application 4 | alias(libs.plugins.jvm) 5 | alias(libs.plugins.serialization) 6 | 7 | alias(libs.plugins.shadow) 8 | } 9 | 10 | group = "cc.unitmesh" 11 | version = "0.1.1" 12 | 13 | repositories { 14 | mavenCentral() 15 | mavenLocal() 16 | maven { 17 | url = uri("https://repo.spring.io/snapshot") 18 | name = "Spring Snapshots" 19 | } 20 | maven(url = uri("https://packages.jetbrains.team/maven/p/ktls/maven")) 21 | } 22 | 23 | dependencies { 24 | implementation(libs.codedb.checkout) 25 | implementation(libs.archguard.analyser.diffChanges) 26 | 27 | implementation("cc.unitmesh:cocoa-core:0.4.3") 28 | implementation("cc.unitmesh:git-differ:0.4.3") 29 | implementation("cc.unitmesh:code-splitter:0.4.3") 30 | implementation("cc.unitmesh:git-commit-message:0.4.3") 31 | 32 | implementation("cc.unitmesh:connection:0.4.3") 33 | implementation("cc.unitmesh:openai:0.4.3") 34 | 35 | // implementation("cc.unitmesh:sentence-transformers:0.4.3") 36 | // implementation("cc.unitmesh:store-elasticsearch:0.4.3") 37 | 38 | implementation("cc.unitmesh:document:0.4.3") 39 | 40 | implementation(libs.kaml) 41 | implementation(libs.github.api) 42 | implementation(libs.gitlab4j.api) 43 | 44 | implementation(libs.clikt) 45 | implementation(libs.rxjava3) 46 | 47 | implementation(libs.kotlin.stdlib) 48 | implementation(libs.serialization.json) 49 | implementation(libs.logging.slf4j.api) 50 | 51 | testImplementation(libs.bundles.test) 52 | testRuntimeOnly(libs.test.junit.engine) 53 | } 54 | 55 | 56 | application { 57 | mainClass.set("cc.unitmesh.genius.MainKt") 58 | } 59 | 60 | tasks { 61 | shadowJar { 62 | manifest { 63 | attributes(Pair("Main-Class", "cc.unitmesh.genius.MainKt")) 64 | } 65 | } 66 | 67 | withType { 68 | useJUnitPlatform() 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /connection.template.yml: -------------------------------------------------------------------------------- 1 | name: mock_response 2 | type: MockLlm 3 | configs: 4 | api-response: "{\"text\": \"this is a mock resource\"}" 5 | -------------------------------------------------------------------------------- /devops-genius.yml: -------------------------------------------------------------------------------- 1 | name: "ChocolateFactory" 2 | path: "." # relative to the project root, or GitHub repo, like "unitmesh/chocolate-factory" 3 | 4 | # store the changelog in the repo 5 | store: 6 | indexName: "unitmesh/chocolate-factory" # default to github repo 7 | 8 | kanban: 9 | type: GitHub 10 | 11 | connection: connection.template.yml 12 | 13 | commitLog: 14 | ignoreType: [ "chore", "docs", "style" ] 15 | ignorePatterns: 16 | - "**/*.md" 17 | - "**/*.json" 18 | - "**/*.yml" 19 | - "**/*.yaml" 20 | - "**/*.vm" 21 | - ".gitignore" 22 | -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | package-lock.json 3 | package.json -------------------------------------------------------------------------------- /docs/CNAME: -------------------------------------------------------------------------------- 1 | devops.unitmesh.cc 2 | -------------------------------------------------------------------------------- /docs/_config.yml: -------------------------------------------------------------------------------- 1 | remote_theme: pmarsceill/just-the-docs 2 | 3 | title: DevOpsGenius 4 | description: DevOpsGenius 旨在结合 LLM 重塑软件开发中的 DevOps 实践。将 LLM 视为团队的初级 “打杂工”,为团队提供各类辅助能力,以提高开发流程的效率和质量。 5 | 6 | heading_anchors: true 7 | 8 | footer_content: "This code is distributed under the MPL 2.0 license. See `LICENSE` in this directory." 9 | 10 | # Footer last edited timestamp 11 | last_edit_timestamp: true # show or hide edit time - page must have `last_modified_date` defined in the frontmatter 12 | last_edit_time_format: "%b %e %Y at %I:%M %p" # uses ruby's time format: https://ruby-doc.org/stdlib-2.7.0/libdoc/time/rdoc/Time.html 13 | 14 | # Footer "Edit this page on GitHub" link text 15 | gh_edit_link: true # show or hide edit this page link 16 | gh_edit_link_text: "Edit this page on GitHub." 17 | gh_edit_repository: "https://github.com/unit-mesh/devops-genius" # the github URL for your repo 18 | gh_edit_branch: "master" # the branch that your docs are served from 19 | gh_edit_source: docs # the source that your files originate from 20 | gh_edit_view_mode: "tree" # "tree" or "edit" if you want the user to jump into the editor immediately 21 | 22 | callouts_level: quiet # or loud 23 | callouts: 24 | highlight: 25 | color: yellow 26 | important: 27 | title: Important 28 | color: blue 29 | new: 30 | title: New 31 | color: green 32 | note: 33 | title: Note 34 | color: purple 35 | warning: 36 | title: Warning 37 | color: red 38 | 39 | # Enable or disable the site search 40 | # Supports true (default) or false 41 | search_enabled: true 42 | 43 | search: 44 | # Split pages into sections that can be searched individually 45 | # Supports 1 - 6, default: 2 46 | heading_level: 4 47 | # Maximum amount of previews per search result 48 | # Default: 3 49 | previews: 3 50 | # Maximum amount of words to display before a matched word in the preview 51 | # Default: 5 52 | preview_words_before: 5 53 | # Maximum amount of words to display after a matched word in the preview 54 | # Default: 10 55 | preview_words_after: 10 56 | # Set the search token separator 57 | # Default: /[\s\-/]+/ 58 | # Example: enable support for hyphenated search words 59 | tokenizer_separator: /[\s/]+/ 60 | # Display the relative url in search results 61 | # Supports true (default) or false 62 | rel_url: true 63 | # Enable or disable the search button that appears in the bottom right corner of every page 64 | # Supports true or false (default) 65 | button: false 66 | 67 | 68 | # Back to top link 69 | back_to_top: true 70 | back_to_top_text: "Back to top" 71 | 72 | # Google Analytics Tracking (optional) 73 | # e.g, UA-1234567-89 74 | #ga_tracking: G-JR0EZ3NLE1 75 | #ga_tracking_anonymize_ip: true # Use GDPR compliant Google Analytics settings (true by default) 76 | 77 | url: "https://devops.unitmesh.cc" # the base hostname & protocol for your site 78 | 79 | # Aux links for the upper right navigation 80 | aux_links: 81 | "View in on GitHub": 82 | - "//github.com/unit-mesh/devops-genius" 83 | 84 | # Makes Aux links open in a new tab. Default is false 85 | aux_links_new_tab: true 86 | 87 | mermaid: 88 | # Version of mermaid library 89 | # Pick an available version from https://cdn.jsdelivr.net/npm/mermaid/ 90 | version: "10.4.0" 91 | 92 | 93 | plugins: 94 | - jekyll-seo-tag 95 | - jekyll-sitemap 96 | -------------------------------------------------------------------------------- /docs/business/business-review.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Business Review 4 | nav_order: 10 5 | has_children: true 6 | permalink: /business 7 | --- 8 | 9 | 根据用户的问题,分析现在遗留系统中的业务。 10 | 11 | ## 策略 12 | 13 | 分析用户的原始问题,获得 DSL,结合 Semantic Code Search 的方式,给出最后的结果。 14 | -------------------------------------------------------------------------------- /docs/ci/ci.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: CI/CD 4 | nav_order: 10 5 | has_children: true 6 | permalink: /ci-cd 7 | --- 8 | 9 | ## CI/CD 10 | 11 | 读取和生成项目的构建信息,如 Gradle、Maven、Makefile 等,再结合对应的构建工具,生成对应的 CI/CD 脚本,如 Jenkinsfile、.gitlab-ci.yml、.github/workflows 等。 12 | 13 | Jenkins file: [https://www.jenkins.io/doc/pipeline/examples/](https://www.jenkins.io/doc/pipeline/examples/) 14 | -------------------------------------------------------------------------------- /docs/code-review/code-review.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Code Review 4 | nav_order: 90 5 | has_children: true 6 | permalink: /code-review 7 | --- 8 | 9 | # CodeReview Design 10 | 11 | ```mermaid 12 | graph TD 13 | A[开始] --> B[获取 commit messages] 14 | B --> C[解析 commit messages] 15 | C --> D[通过 commit type 过滤不需要 review 的 commits] 16 | D --> E[获取 User Stories 标题] 17 | E --> F[生成 patch 文件] 18 | F --> G[过滤不需要 review 的文件] 19 | G --> H[交由 LLM 处理] 20 | ``` 21 | 22 | ## Prompt 策略 23 | 24 | 1. 如果变更的代码行数少,则只审核业务含义 —— 根据提交信息,解析对应的 story 名称,然后进行检查。 25 | 2. 根据变更的代码,生成对应的代码信息,作为上下文的一部分。 26 | 3. 如果变更的行数多,则需要进行代码逻辑的检查,以及对应的语法检查。 27 | 4. 如果单次变更的行数过多,则需要进行拆分。 28 | 29 | ### M1:提交格式解析 30 | 31 | 格式:Conventional Commits 32 | 33 | 解析库:[git-commit-message](https://github.com/unit-mesh/chocolate-factory/tree/master/code-modules/git-commit-message) (Chocolate Factory) 34 | 35 | 标准格式: 36 | 37 | ``` 38 | [optional scope]: 39 | 40 | [optional description] 41 | [optional footer(s)] 42 | ``` 43 | 44 | 示例: 45 | 46 | ``` 47 | feat(ng-list): Allow custom separator Closes #123 Closes #25 Fixes #33 48 | ``` 49 | 50 | 会生成三个 CommitReference:123,25,33 51 | 52 | ### M1:条件过滤 53 | 54 | 可配置的条件过滤 55 | 56 | 1. 根据提交信息中的 type 过滤,如忽略:docs, chore, style 等。 57 | 2. 根据文件路径过滤,如忽略:.md, .json 等。 58 | 59 | ### M1:基本的 Patch 优化 60 | 61 | 1. 如果变更的代码行数少,则只审核业务含义。 62 | 2. 处理文件目录移动,文件重命名的情况。(即忽略文件的变更) 63 | 3. 使用传统工具,检测语法问题,诸如 pre-commit 的情况。 64 | 65 | ### M2:Patch 优化 66 | 67 | 1. 如果超过 10 个文件,则需要拆分。 68 | - 忽略数据文件。 69 | - 忽略配置文件。 70 | 2. 如果单个行数变更大,则直接 review 原函数。 71 | 72 | ### M2:重写比例 73 | 74 | 1. 如果重写比例过高,则需要进行代码逻辑的检查,结合更多的上下文。(重写比例:重写的代码行数 / 总代码行数,建议小于 0.5,行数大于 75 | 30 / 2 行) 76 | 2. 当出现重大变化时,建议进行人工检查。 77 | -------------------------------------------------------------------------------- /docs/code-review/prompt-design.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Prompt Sample 4 | parent: Code Review 5 | nav_order: 90 6 | permalink: /code-review/prompt 7 | --- 8 | 9 | You are a senior software developer, who can help me do code review a commit. 10 | 11 | Use the following response format, keeping the section headings as-is, and provide 12 | your feedback. Use bullet points for each response. The provided examples are for 13 | illustration purposes only and should not be repeated. 14 | 15 | **Syntax and logical errors (example)**: 16 | - Incorrect indentation on line 12 17 | - Missing closing parenthesis on line 23 18 | 19 | **Code refactoring and quality (example)**: 20 | - Replace multiple if-else statements with a switch case for readability 21 | - Extract repetitive code into separate functions 22 | 23 | **Performance optimization (example)**: 24 | - Use a more efficient sorting algorithm to reduce time complexity 25 | - Cache results of expensive operations for reuse 26 | 27 | **Security vulnerabilities (example)**: 28 | - Sanitize user input to prevent SQL injection attacks 29 | - Use prepared statements for database queries 30 | 31 | **Best practices (example)**: 32 | - Add meaningful comments and documentation to explain the code 33 | - Follow consistent naming conventions for variables and functions 34 | 35 | Business Context: 36 | 37 | Commit Message: chore: disable some gradle dep for small package size 38 | 39 | Code Changes: index 886725b..af1246e 100644 40 | --- a/build.gradle.kts 41 | +++ b/build.gradle.kts 42 | @@ -8,7 +8,7 @@ 43 | } 44 | 45 | group = "cc.unitmesh" 46 | -version = "0.1.0" 47 | +version = "0.1.1" 48 | 49 | repositories { 50 | mavenCentral() 51 | 52 | 53 | 作为您的 Tech Lead,我只关注一些关键的代码审查问题。请为我提供一个关键摘要,按照以下格式: 54 | 55 | 关键摘要: // 你应该使用中文来回答,合并相似的问题 56 | 是否建议立即修改: // 是/否 57 | 58 | -------------------------------------------------------------------------------- /docs/code-review/research.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Code Review Research 4 | parent: Code Review 5 | nav_order: 90 6 | permalink: /code-review/research 7 | --- 8 | 9 | # Code Review Research 10 | 11 | 12 | ## Article 13 | 14 | [团队的Code Review实践](https://www.thoughtworks.com/zh-cn/insights/blog/agile-engineering-practices/how-to-code-review](https://www.thoughtworks.com/zh-cn/insights/blog/agile-engineering-practices/how-to-code-review) 15 | 16 | - 互相学习,知识共享 17 | - 统一风格,提高代码质量 18 | - 尽早暴露问题,降低修复成本 19 | 20 | 工具:[https://github.com/MTWGA/thoughtworks-code-review-tools](https://github.com/MTWGA/thoughtworks-code-review-tools) 21 | 22 | 23 | ## Paper 24 | 25 | ### Expectations, outcomes, and challenges of modern code review 26 | 27 | 从我们的研究中,我们为开发人员提出以下建议: 28 | 29 | - 质量保证:代码审查的期望与实际结果存在不匹配。根据我们的研究,审查通常不如项目成员期望的那样频繁地发现缺陷,尤其是深层次、微妙或“宏观”层面的问题。以这种方式依赖代码审查来确保质量可能会面临风险。 30 | - 理解:当审阅者在上下文和代码方面具有先验知识时,他们能够更快地完成审查并向作者提供更有价值的反馈。团队应该努力提高开发人员的广泛理解(如果更改的作者是唯一的专家,她就没有潜在的审阅者),在使用审查来发现缺陷时,更改的作者应该尽可能包括代码所有者和其他理解代码的人。开发人员表示,当作者在审查中向他们提供上下文和指导时,他们可以更好、更快地做出反应。 31 | - 超越缺陷:现代代码审查提供了除了发现缺陷以外的好处。代码审查可以用于改善代码风格,寻找替代解决方案,增加学习,分享代码所有权等。这应该指导代码审查政策。 32 | - 沟通:尽管有支持代码审查的工具不断增长,开发人员在审查时仍需要比注释更丰富的沟通方式。团队应该提供面对面或至少同步沟通的机制。 33 | 34 | ### Modern code review: a case study at google 35 | 36 | At Google, over 35% of the changes under consideration modify only a single file and about 90% modify fewer than 10 files. 37 | Over 10% of changes modify only a single line of code, and the median number of lines modified is 24. The median change size 38 | is significantly lower than reported by Rigby and Bird for companies such as AMD (44 lines), Lucent (263 lines), 39 | and Bing, Office and SQL Server at Microsoft (somewhere between those boundaries), 40 | but in line for change sizes in open source projects. 41 | 42 | 在谷歌,有超过35%的正在考虑的更改仅修改一个文件,约90%的更改修改不到10个文件。 43 | 超过10%的更改仅修改一行代码,中位数修改的行数为24。中位数的更改大小明显低于Rigby和Bird为像AMD(44行)、Lucent(263行)以及微软的Bing、 44 | Office和SQL Server(在这些边界之间)等公司报告的更改大小,但与开源项目的更改大小保持一致。 45 | 46 | ### Pre Review 47 | 48 | 简介一下:业务上下文 49 | 50 | # Prompt Demo 51 | 52 | ## JetBrains Explain AI 53 | 54 | You are an senior software developer who can help me understand a commit with business. 55 | Explain this commit. 56 | Do not mention filenames. 57 | Ignore any changes to imports and requires. 58 | Keep the explanation under five sentences. Don't explain changes in test files. 59 | 60 | Message: Use freeCompilerArgs += "-Xjsr305=strict" 61 | 62 | See https://youtrack.jetbrains.com/issue/KT-41985 63 | 64 | Changes: 65 | 66 | Index: README.adoc 67 | =================================================================== 68 | --- a/README.adoc (revision b6ed535e3d4b6734a5695c32cc23ce8d5524b3eb) 69 | +++ b/README.adoc (revision 0906a3d831fea14898e4f0914d6b64531f6c3ade) 70 | @@ -103,7 +103,7 @@ 71 | ---- 72 | tasks.withType { 73 | kotlinOptions { 74 | - freeCompilerArgs = listOf("-Xjsr305=strict") 75 | + freeCompilerArgs += "-Xjsr305=strict" 76 | } 77 | } 78 | ---- 79 | Index: build.gradle.kts 80 | =================================================================== 81 | --- a/build.gradle.kts (revision b6ed535e3d4b6734a5695c32cc23ce8d5524b3eb) 82 | +++ b/build.gradle.kts (revision 0906a3d831fea14898e4f0914d6b64531f6c3ade) 83 | @@ -39,7 +39,7 @@ 84 | 85 | tasks.withType { 86 | kotlinOptions { 87 | - freeCompilerArgs = listOf("-Xjsr305=strict") 88 | + freeCompilerArgs += "-Xjsr305=strict" 89 | } 90 | } 91 | 92 | 93 | ## AutoDev 94 | 95 | You are a seasoned software developer, and I'm seeking your expertise to review the following code: 96 | 97 | - Please provide an overview of the business objectives and the context behind this commit. This will ensure that the code aligns with the project's requirements and goals. 98 | - Focus on critical algorithms, logical flow, and design decisions within the code. Discuss how these changes impact the core functionality and the overall structure of the code. 99 | - Identify and highlight any potential issues or risks introduced by these code changes. This will help reviewers pay special attention to areas that may require improvement or further analysis. 100 | - Emphasize the importance of compatibility and consistency with the existing codebase. Ensure that the code adheres to the established standards and practices for code uniformity and long-term maintainability. 101 | - Lastly, provide a concise high-level summary that encapsulates the key aspects of this commit. This summary should enable reviewers to quickly grasp the major changes in this update. 102 | 103 | PS: Your insights and feedback are invaluable in ensuring the quality and reliability of this code. Thank you for your assistance. 104 | Commit Message: feat: update test for samples\n\nCode Changes:\n\nIndex: build.gradle.kts 105 | --- a/build.gradle.kts 106 | +++ b/build.gradle.kts 107 | @@ -6,7 +6,6 @@ 108 | 109 | group = "cc.unitmesh.untitled" 110 | version = "0.0.1-SNAPSHOT" 111 | -java.sourceCompatibility = JavaVersion.VERSION_17 112 | 113 | repositories { 114 | mavenCentral() 115 | @@ -17,6 +16,7 @@ 116 | implementation("org.springframework.boot:spring-boot-starter-jdbc") 117 | implementation("org.springframework.boot:spring-boot-starter-data-jdbc") 118 | implementation("org.springframework.boot:spring-boot-starter-data-jpa") 119 | + testImplementation("junit:junit:4.13.1") 120 | 121 | developmentOnly("org.springframework.boot:spring-boot-devtools") 122 | 123 | Index: src/main/java/cc/unitmesh/untitled/demo/controller/BlogController.java 124 | --- a/src/main/java/cc/unitmesh/untitled/demo/controller/BlogController.java 125 | +++ b/src/main/java/cc/unitmesh/untitled/demo/controller/BlogController.java 126 | @@ -19,7 +19,12 @@ 127 | this.blogService = blogService; 128 | } 129 | 130 | - // create blog 131 | + @ApiOperation(value = "Get Blog by id") 132 | + @GetMapping("/{id}") 133 | + public BlogPost getBlog(@PathVariable Long id) { 134 | + return blogService.getBlogById(id); 135 | + } 136 | + 137 | @ApiOperation(value = "Create a new blog") 138 | @PostMapping("/") 139 | public BlogPost createBlog(@RequestBody CreateBlogRequest request) { 140 | Index: src/main/java/cc/unitmesh/untitled/demo/entity/BlogPost.java 141 | --- a/src/main/java/cc/unitmesh/untitled/demo/entity/BlogPost.java 142 | +++ b/src/main/java/cc/unitmesh/untitled/demo/entity/BlogPost.java 143 | @@ -25,6 +25,10 @@ 144 | 145 | } 146 | 147 | + public void setId(Long id) { 148 | + this.id = id; 149 | + } 150 | + 151 | public Long getId() { 152 | return this.id; 153 | } 154 | Index: src/test/java/cc/unitmesh/untitled/demo/controller/BlogControllerTest.java 155 | --- a/src/test/java/cc/unitmesh/untitled/demo/controller/BlogControllerTest.java 156 | +++ b/src/test/java/cc/unitmesh/untitled/demo/controller/BlogControllerTest.java 157 | @@ -1,19 +1,43 @@ 158 | package cc.unitmesh.untitled.demo.controller; 159 | 160 | +import cc.unitmesh.untitled.demo.entity.BlogPost; 161 | +import cc.unitmesh.untitled.demo.repository.BlogRepository; 162 | import org.junit.jupiter.api.Test; 163 | +import org.mockito.Mockito; 164 | import org.springframework.beans.factory.annotation.Autowired; 165 | +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; 166 | import org.springframework.boot.test.context.SpringBootTest; 167 | +import org.springframework.boot.test.mock.mockito.MockBean; 168 | import org.springframework.test.web.servlet.MockMvc; 169 | 170 | +import java.util.Optional; 171 | + 172 | +import static org.hamcrest.Matchers.containsString; 173 | +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; 174 | +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; 175 | +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; 176 | + 177 | @SpringBootTest 178 | +@AutoConfigureMockMvc 179 | class BlogControllerTest { 180 | 181 | - @SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection") 182 | @Autowired 183 | private MockMvc mockMvc; 184 | + 185 | + @MockBean 186 | + private BlogRepository blogRepository; 187 | + 188 | @Test 189 | - void should_get_blog_one_when_has_blog() throws Exception { 190 | + public void should_return_correct_blog_information_when_post_item() throws Exception { 191 | + BlogPost mockBlog = new BlogPost("Test Title", "Test Content", "Test Author"); 192 | + mockBlog.setId(1L); 193 | 194 | + Mockito.when(blogRepository.findById(1L)).thenReturn(Optional.of(mockBlog)); 195 | 196 | + mockMvc.perform(get("/blog/1")) 197 | + .andExpect(status().isOk()) 198 | + .andExpect(content().string(containsString("Test Title"))) 199 | + .andExpect(content().string(containsString("Test Content"))); 200 | } 201 | -} 202 | \ No newline at end of file 203 | +} 204 | + 205 | 206 | 207 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Home 4 | description: DevOpsGenius 旨在结合 LLM 重塑软件开发中的 DevOps 实践。将 LLM 视为团队的初级 “打杂工”,为团队提供各类辅助能力,以提高开发流程的效率和质量。 5 | nav_order: 1 6 | permalink: / 7 | --- 8 | 9 |

DevOps Genius

10 |

11 | logo 12 |

13 |

14 | 15 | CI/CD 16 | 17 | 18 | Powered By 19 | 20 |

21 | 22 | [Read the docs →](https://devops.unitmesh.cc/) 23 | 24 | DevOpsGenius 旨在结合 LLM 重塑软件开发中的 DevOps 实践。将 LLM 视为团队的初级 “打杂工”,为团队提供各类辅助能力,以提高开发流程的效率和质量。 25 | 26 | - **自动 CI/CD 脚本生成**:DevOpsGenius 能够自动分析代码仓库,生成对应的 CI/CD 脚本,以实现自动化的构建、测试和部署流程。 27 | - **自动代码评审**:DevOpsGenius 使用先进的代码分析和静态分析技术,自动检测潜在的问题、错误和不规范的代码风格,并提供有针对性的建议和改进意见。 28 | - **自动需求优化**:DevOpsGenius 能够自动分析需求,识别需求中的问题和不足,并提供有针对性的建议和改进意见。 29 | - **智能拉取请求处理**:DevOpsGenius 能够智能地审查和处理拉取请求。它自动识别代码变更、冲突和合并请求,并以自动化的方式进行验证、测试和部署流程,以确保高质量的代码交付。 30 | - **自动 bug 分析与修复建议**:DevOpsGenius 能够自动分析 bug,识别 bug 中的问题和不足,并提供有针对性的建议和改进意见。 31 | - **智能报告和统计**:DevOpsGenius生成详细的报告和统计数据,展示代码质量、团队绩效和项目进度等关键指标。这些洞察力有助于团队进行数据驱动的决策,优化开发流程和资源分配。 32 | -------------------------------------------------------------------------------- /docs/issue/issue.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Issue Analysis 4 | nav_order: 30 5 | has_children: true 6 | permalink: /business 7 | --- 8 | 9 | 根据用户的问题,分析现在遗留系统中的业务。 10 | 11 | ## 策略 12 | 13 | 分析用户的原始问题,获得 DSL,结合 Semantic Code Search 的方式,给出最后的结果。 14 | -------------------------------------------------------------------------------- /docs/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Group 4 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /docs/usage.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Usage 4 | nav_order: 1 5 | permalink: /usage 6 | --- 7 | 8 | # Usage 9 | 10 | ## QuickStart 11 | 12 | 1. 下载 CLI:https://github.com/unit-mesh/devops-genius/releases 13 | 2. 配置 `devops-genius.yml`,参考 [配置文件](#配置文件) 14 | 15 | ### 配置文件 16 | 17 | ```yaml 18 | name: "ChocolateFactory" 19 | repo: "." # relative to the project root, or GitHub repo, like "unitmesh/chocolate-factory" 20 | 21 | # 配置对应的模型 22 | connection: connection.yml 23 | 24 | # store the changelog in the repo 25 | store: 26 | indexName: "unitmesh/chocolate-factory" # default to github repo 27 | 28 | # 用于获取需求,关联到对应的 issue/用户故事/特征 29 | kanban: 30 | type: GitHub 31 | token: "xxx" 32 | 33 | # 根据 commit message 忽略对应的提交和文件 34 | commitLog: 35 | ignoreType: [ "chore", "docs", "style" ] 36 | ignorePatterns: 37 | - "**/*.md" 38 | - "**/*.json" 39 | - "**/*.yml" 40 | - "**/*.yaml" 41 | - "**/*.vm" 42 | - ".gitignore" 43 | ``` 44 | 45 | ### connection.yml 用于配置对应的模型 46 | 47 | 示例: 48 | 49 | ```yaml 50 | name: open_ai_connection 51 | type: OpenAI 52 | configs: 53 | api-host: https://api.aios.chat/ # 如果有的话 54 | secrets: 55 | api-key: "xxx" 56 | ``` 57 | 58 | 更详细的模型支持见:[https://framework.unitmesh.cc/prompt-script/connection-config](https://framework.unitmesh.cc/prompt-script/connection-config) 59 | -------------------------------------------------------------------------------- /fixtures/example.json: -------------------------------------------------------------------------------- 1 | { 2 | "context": { 3 | "businessContext": "1. 如果变更的代码行数少,则只审核业务含义 —— 根据提交信息,解析对应的 story 名称,然后进行检查。\n2. 根据变更的代码,生成对应的代码信息,作为上下文的一部分。\n3. 如果变更的行数多,则需要进行代码逻辑的检查,以及对应的语法检查。\n4. 如果单次变更的行数过多,则需要进行拆分。\n", 4 | "fullMessage": "feat(differ): add for handle patches match #8", 5 | "changes": "--- /dev/null\n+++ b/code-modules/git-differ/src/main/kotlin/cc/unitmesh/cf/code/ChangedLineCount.kt\n@@ -0,0 +1,9 @@\n+package cc.unitmesh.cf.code\n+\n+import kotlinx.serialization.Serializable\n+\n+@Serializable\n+data class ChangedLineCount(\n+ val added: Int,\n+ val deleted: Int,\n+)\n\nindex 0ea0eb4..efb0fda 100644\n--- a/code-modules/git-differ/src/main/kotlin/cc/unitmesh/cf/code/GitDiffer.kt\n+++ b/code-modules/git-differ/src/main/kotlin/cc/unitmesh/cf/code/GitDiffer.kt\n@@ -39,7 +39,7 @@\n val functionName: String = \"\",\n val code: String = \"\",\n val addedLines: Int = 0,\n- val deletedLines: Int = 0\n+ val deletedLines: Int = 0,\n )\n \n /**\n@@ -108,73 +108,87 @@\n * @param untilRev The revision to end at.\n * @return A map containing the file paths as keys and the corresponding patch text as values.\n */\n- fun patchBetween(sinceRev: String, untilRev: String): Map {\n- git.use {\n- // 获取 sinceRev 和 untilRev 的 ObjectId\n- val sinceObj: ObjectId = repository.resolve(sinceRev)\n- val untilObj: ObjectId = repository.resolve(untilRev)\n+ fun patchBetween(sinceRev: String, untilRev: String): Map {\n+ // 获取 sinceRev 和 untilRev 的 ObjectId\n+ val sinceObj: ObjectId = repository.resolve(sinceRev)\n+ val untilObj: ObjectId = repository.resolve(untilRev)\n \n- // 获取两个提交之间的差异(补丁)\n- val outputStream = ByteArrayOutputStream()\n- val diffFormatter = DiffFormatter(outputStream)\n- diffFormatter.setRepository(repository)\n- diffFormatter.format(sinceObj, untilObj)\n+ // 获取两个提交之间的差异(补丁)\n+ val outputStream = ByteArrayOutputStream()\n+ val diffFormatter = DiffFormatter(outputStream)\n+ diffFormatter.setRepository(repository)\n+ diffFormatter.format(sinceObj, untilObj)\n \n- summaryFileDiff(diffFormatter, sinceObj, untilObj)\n+ val diffs: List = diffFormatter.scan(sinceObj, untilObj)\n+ val patchMap = mutableMapOf()\n \n- // 将补丁转换为 Map\n- val patchMap = mutableMapOf()\n- outputStream.toString().split(\"\nnew file mode 100644\nindex 0000000..d29effd\n--- /dev/null\n+++ b/code-modules/git-differ/src/main/kotlin/cc/unitmesh/cf/code/OptimizePatch.kt\n@@ -0,0 +1,35 @@\n+package cc.unitmesh.cf.code\n+\n+import kotlinx.serialization.Serializable\n+import org.eclipse.jgit.diff.DiffEntry\n+\n+@Serializable\n+data class OptimizePatch(\n+ val changedLineCount: ChangedLineCount,\n+ val changeType: PatchChangeType,\n+ val content: String,\n+ val path: String,\n+)\n+\n+/**\n+ * from [org.eclipse.jgit.diff.DiffEntry.ChangeType]\n+ */\n+enum class PatchChangeType {\n+ ADD,\n+ MODIFY,\n+ DELETE,\n+ RENAME,\n+ COPY;\n+\n+ companion object {\n+ fun from(changeType: DiffEntry.ChangeType): PatchChangeType {\n+ return when (changeType) {\n+ DiffEntry.ChangeType.ADD -> ADD\n+ DiffEntry.ChangeType.MODIFY -> MODIFY\n+ DiffEntry.ChangeType.DELETE -> DELETE\n+ DiffEntry.ChangeType.RENAME -> RENAME\n+ DiffEntry.ChangeType.COPY -> COPY\n+ }\n+ }\n+ }\n+}\n" 6 | } 7 | } -------------------------------------------------------------------------------- /fixtures/prompt.unit-mesh.yml: -------------------------------------------------------------------------------- 1 | name: "Code Review" 2 | description: "Verify code review template" 3 | 4 | jobs: 5 | prompt-evaluate: # job name should be unique for each job 6 | description: "Evaluate prompt with different parameters" 7 | connection: openai-connection.yml 8 | template: ../src/main/resources/code-review/simple-review.open-ai.vm 9 | template-datasource: 10 | - type: file 11 | value: example.json 12 | 13 | strategy: 14 | - type: connection 15 | value: 16 | - type: range 17 | key: temperature 18 | range: 0.0~0.0 19 | step: 0.1 20 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # This file was generated by the Gradle 'init' task. 2 | # https://docs.gradle.org/current/userguide/build_environment.html#sec:gradle_configuration_properties 3 | 4 | org.gradle.parallel=true 5 | org.gradle.caching=true 6 | 7 | -------------------------------------------------------------------------------- /gradle/libs.versions.toml: -------------------------------------------------------------------------------- 1 | [versions] 2 | kotlin = "1.9.10" 3 | 4 | kotlinxCoroutines = "1.7.3" 5 | kotlinxSerialization = "1.6.0" 6 | 7 | 8 | junit = "5.9.1" 9 | kotlinTest = "5.5.4" 10 | mockk = "1.13.5" 11 | assertj = "3.22.0" 12 | 13 | logback = "1.4.5" 14 | slf4j = "2.0.5" 15 | 16 | chapi = "2.1.3" 17 | archguard = "2.0.7" 18 | codedb = "0.1.2" 19 | 20 | gson = "2.10.1" 21 | shadowJar = "8.1.1" 22 | 23 | 24 | jgit = "6.7.0.202309050840-r" 25 | 26 | cf = "0.4.0" 27 | 28 | [libraries] 29 | kotlin-stdlib = { group = "org.jetbrains.kotlin", name = "kotlin-stdlib", version.ref = "kotlin" } 30 | kotlin-stdlib-jdk8 = { group = "org.jetbrains.kotlin", name = "kotlin-stdlib-jdk8", version.ref = "kotlin" } 31 | 32 | kotlin-scripting-jvm = { group = "org.jetbrains.kotlin", name = "kotlin-scripting-jvm", version.ref = "kotlin" } 33 | kotlin-reflect = { group = "org.jetbrains.kotlin", name = "kotlin-reflect", version.ref = "kotlin" } 34 | reflections = { group = "org.reflections", name = "reflections", version = "0.10.2" } 35 | kotlin-test = { group = "org.jetbrains.kotlin", name = "kotlin-test", version.ref = "kotlin" } 36 | 37 | dataframe = { group = "org.jetbrains.kotlinx", name = "dataframe", version = "0.11.1" } 38 | 39 | # Kotlinx Serialization 40 | serialization-json = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-json", version.ref = "kotlinxSerialization" } 41 | 42 | # Coroutines 43 | coroutines-core = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-core", version.ref = "kotlinxCoroutines" } 44 | 45 | 46 | # Testing 47 | test-junit-api = { group = "org.junit.jupiter", name = "junit-jupiter-api", version.ref = "junit" } 48 | test-junit-params = { group = "org.junit.jupiter", name = "junit-jupiter-params", version.ref = "junit" } 49 | test-junit-engine = { group = "org.junit.jupiter", name = "junit-jupiter-engine", version.ref = "junit" } 50 | test-kotlintest-assertions = { module = "io.kotest:kotest-assertions-core", version.ref = "kotlinTest" } 51 | test-mockk = { group = "io.mockk", name = "mockk", version.ref = "mockk" } 52 | test-assertj = { group = "org.assertj", name = "assertj-core", version.ref = "assertj" } 53 | 54 | # Logging 55 | logging-slf4j-api = { group = "org.slf4j", name = "slf4j-api", version.ref = "slf4j" } 56 | logging-logback-classic = { group = "ch.qos.logback", name = "logback-classic", version.ref = "logback" } 57 | 58 | # chapi 59 | chapi-domain = { group = "com.phodal.chapi", name = "chapi-domain", version.ref = "chapi" } 60 | chapi-java = { group = "com.phodal.chapi", name = "chapi-ast-java", version.ref = "chapi" } 61 | chapi-kotlin = { group = "com.phodal.chapi", name = "chapi-ast-kotlin", version.ref = "chapi" } 62 | 63 | # archguard 64 | # implementation("org.archguard.aaac:lang:2.0.0-beta.5") 65 | archguard-scanner-core = { group = "org.archguard.scanner", name = "scanner_core", version.ref = "archguard" } 66 | archguard-dsl = { group = "org.archguard.aaac", name = "dsl", version.ref = "archguard" } 67 | archguard-analyser-sca = { group = "org.archguard.scanner", name = "analyser_sca", version.ref = "archguard" } 68 | ## analyser_diff_changes 69 | archguard-analyser-diffChanges = { group = "org.archguard.scanner", name = "analyser_diff_changes", version.ref = "archguard" } 70 | archguard-lang-kotlin = { group = "org.archguard.scanner", name = "lang_kotlin", version.ref = "archguard" } 71 | archguard-lang-java = { group = "org.archguard.scanner", name = "lang_java", version.ref = "archguard" } 72 | archguard-lang-python = { group = "org.archguard.scanner", name = "lang_python", version.ref = "archguard" } 73 | archguard-lang-typescript = { group = "org.archguard.scanner", name = "lang_typescript", version.ref = "archguard" } 74 | archguard-lang-golang = { group = "org.archguard.scanner", name = "lang_golang", version.ref = "archguard" } 75 | 76 | archguard-feat-apicalls = { group = "org.archguard.scanner", name = "feat_apicalls", version.ref = "archguard" } 77 | archguard-feat-datamap = { group = "org.archguard.scanner", name = "feat_datamap", version.ref = "archguard" } 78 | codedb-checkout = { group = "org.archguard.codedb", name = "checkout", version.ref = "codedb" } 79 | 80 | gson = { group = "com.google.code.gson", name = "gson", version.ref = "gson" } 81 | 82 | # llm part 83 | commonmark-core = "org.commonmark:commonmark:0.21.0" 84 | commonmark-gfm-tables = "org.commonmark:commonmark-ext-gfm-tables:0.21.0" 85 | commonmark-heading-anchor = "org.commonmark:commonmark-ext-gfm-tables:0.21.0" 86 | 87 | openai-gpt3 = "com.theokanning.openai-gpt3-java:service:0.14.0" 88 | retrofit2-converter-jackson = "com.squareup.retrofit2:converter-jackson:2.9.0" 89 | azure-openai = "com.azure:azure-ai-openai:1.0.0-beta.3" 90 | 91 | kaml = "com.charleskorn.kaml:kaml:0.55.0" 92 | 93 | github-api = "org.kohsuke:github-api:1.314" 94 | gitlab4j-api = "org.gitlab4j:gitlab4j-api:5.3.0" 95 | 96 | jackson-module-kotlin = "com.fasterxml.jackson.module:jackson-module-kotlin:2.14.2" 97 | jackson-module-jsonSchema = "com.fasterxml.jackson.module:jackson-module-jsonSchema:2.14.2" 98 | jackson-dataformat-yaml = "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.14.2" 99 | jackson-databind = "com.fasterxml.jackson.core:jackson-databind:2.14.2" 100 | 101 | 102 | rxjava3 = "io.reactivex.rxjava3:rxjava:3.1.7" 103 | 104 | kotlinx-datetime = "org.jetbrains.kotlinx:kotlinx-datetime:0.4.1" 105 | clikt = "com.github.ajalt.clikt:clikt:4.2.0" 106 | 107 | jgit = { group = "org.eclipse.jgit", name = "org.eclipse.jgit", version.ref = "jgit" } 108 | 109 | # implementation(libs.cocoaCore) 110 | cf-cocoa-core = { group = "cc.unitmesh", name = "cocoa-core", version.ref = "cf" } 111 | # implementation(libs.codeModules.gitDiffer) 112 | cf-sentence-transformers = { group = "cc.unitmesh", name = "sentence-transformers", version.ref="cf" } 113 | # implementation(libs.codedb.checkout) 114 | # implementation(libs.archguard.analyser.diffChanges) 115 | 116 | 117 | 118 | [plugins] 119 | jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } 120 | multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" } 121 | serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" } 122 | shadow = { id = "com.github.johnrengelman.shadow", version.ref = "shadowJar" } 123 | 124 | [bundles] 125 | test = ["test-junit-params", "kotlin-test", "test-kotlintest-assertions", "test-mockk", "test-assertj"] 126 | 127 | jackson = ["jackson-module-jsonSchema", "jackson-module-kotlin", "jackson-dataformat-yaml"] 128 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unit-mesh/devops-genius/bdcbddd9940844fcca88c5e08c20f1d0994fed9f/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015-2021 the original authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | # 21 | # Gradle start up script for POSIX generated by Gradle. 22 | # 23 | # Important for running: 24 | # 25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 26 | # noncompliant, but you have some other compliant shell such as ksh or 27 | # bash, then to run this script, type that shell name before the whole 28 | # command line, like: 29 | # 30 | # ksh Gradle 31 | # 32 | # Busybox and similar reduced shells will NOT work, because this script 33 | # requires all of these POSIX shell features: 34 | # * functions; 35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 37 | # * compound commands having a testable exit status, especially «case»; 38 | # * various built-in commands including «command», «set», and «ulimit». 39 | # 40 | # Important for patching: 41 | # 42 | # (2) This script targets any POSIX shell, so it avoids extensions provided 43 | # by Bash, Ksh, etc; in particular arrays are avoided. 44 | # 45 | # The "traditional" practice of packing multiple parameters into a 46 | # space-separated string is a well documented source of bugs and security 47 | # problems, so this is (mostly) avoided, by progressively accumulating 48 | # options in "$@", and eventually passing that to Java. 49 | # 50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 52 | # see the in-line comments for details. 53 | # 54 | # There are tweaks for specific operating systems such as AIX, CygWin, 55 | # Darwin, MinGW, and NonStop. 56 | # 57 | # (3) This script is generated from the Groovy template 58 | # https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 59 | # within the Gradle project. 60 | # 61 | # You can find Gradle at https://github.com/gradle/gradle/. 62 | # 63 | ############################################################################## 64 | 65 | # Attempt to set APP_HOME 66 | 67 | # Resolve links: $0 may be a link 68 | app_path=$0 69 | 70 | # Need this for daisy-chained symlinks. 71 | while 72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 73 | [ -h "$app_path" ] 74 | do 75 | ls=$( ls -ld "$app_path" ) 76 | link=${ls#*' -> '} 77 | case $link in #( 78 | /*) app_path=$link ;; #( 79 | *) app_path=$APP_HOME$link ;; 80 | esac 81 | done 82 | 83 | # This is normally unused 84 | # shellcheck disable=SC2034 85 | APP_BASE_NAME=${0##*/} 86 | # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) 87 | APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit 88 | 89 | # Use the maximum available, or set MAX_FD != -1 to use that value. 90 | MAX_FD=maximum 91 | 92 | warn () { 93 | echo "$*" 94 | } >&2 95 | 96 | die () { 97 | echo 98 | echo "$*" 99 | echo 100 | exit 1 101 | } >&2 102 | 103 | # OS specific support (must be 'true' or 'false'). 104 | cygwin=false 105 | msys=false 106 | darwin=false 107 | nonstop=false 108 | case "$( uname )" in #( 109 | CYGWIN* ) cygwin=true ;; #( 110 | Darwin* ) darwin=true ;; #( 111 | MSYS* | MINGW* ) msys=true ;; #( 112 | NONSTOP* ) nonstop=true ;; 113 | esac 114 | 115 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 116 | 117 | 118 | # Determine the Java command to use to start the JVM. 119 | if [ -n "$JAVA_HOME" ] ; then 120 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 121 | # IBM's JDK on AIX uses strange locations for the executables 122 | JAVACMD=$JAVA_HOME/jre/sh/java 123 | else 124 | JAVACMD=$JAVA_HOME/bin/java 125 | fi 126 | if [ ! -x "$JAVACMD" ] ; then 127 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 128 | 129 | Please set the JAVA_HOME variable in your environment to match the 130 | location of your Java installation." 131 | fi 132 | else 133 | JAVACMD=java 134 | if ! command -v java >/dev/null 2>&1 135 | then 136 | die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 137 | 138 | Please set the JAVA_HOME variable in your environment to match the 139 | location of your Java installation." 140 | fi 141 | fi 142 | 143 | # Increase the maximum file descriptors if we can. 144 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 145 | case $MAX_FD in #( 146 | max*) 147 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. 148 | # shellcheck disable=SC3045 149 | MAX_FD=$( ulimit -H -n ) || 150 | warn "Could not query maximum file descriptor limit" 151 | esac 152 | case $MAX_FD in #( 153 | '' | soft) :;; #( 154 | *) 155 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. 156 | # shellcheck disable=SC3045 157 | ulimit -n "$MAX_FD" || 158 | warn "Could not set maximum file descriptor limit to $MAX_FD" 159 | esac 160 | fi 161 | 162 | # Collect all arguments for the java command, stacking in reverse order: 163 | # * args from the command line 164 | # * the main class name 165 | # * -classpath 166 | # * -D...appname settings 167 | # * --module-path (only if needed) 168 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 169 | 170 | # For Cygwin or MSYS, switch paths to Windows format before running java 171 | if "$cygwin" || "$msys" ; then 172 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 173 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 174 | 175 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 176 | 177 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 178 | for arg do 179 | if 180 | case $arg in #( 181 | -*) false ;; # don't mess with options #( 182 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 183 | [ -e "$t" ] ;; #( 184 | *) false ;; 185 | esac 186 | then 187 | arg=$( cygpath --path --ignore --mixed "$arg" ) 188 | fi 189 | # Roll the args list around exactly as many times as the number of 190 | # args, so each arg winds up back in the position where it started, but 191 | # possibly modified. 192 | # 193 | # NB: a `for` loop captures its iteration list before it begins, so 194 | # changing the positional parameters here affects neither the number of 195 | # iterations, nor the values presented in `arg`. 196 | shift # remove old arg 197 | set -- "$@" "$arg" # push replacement arg 198 | done 199 | fi 200 | 201 | 202 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 203 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 204 | 205 | # Collect all arguments for the java command; 206 | # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of 207 | # shell script including quotes and variable substitutions, so put them in 208 | # double quotes to make sure that they get re-expanded; and 209 | # * put everything else in single quotes, so that it's not re-expanded. 210 | 211 | set -- \ 212 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 213 | -classpath "$CLASSPATH" \ 214 | org.gradle.wrapper.GradleWrapperMain \ 215 | "$@" 216 | 217 | # Stop when "xargs" is not available. 218 | if ! command -v xargs >/dev/null 2>&1 219 | then 220 | die "xargs is not available" 221 | fi 222 | 223 | # Use "xargs" to parse quoted args. 224 | # 225 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 226 | # 227 | # In Bash we could simply go: 228 | # 229 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 230 | # set -- "${ARGS[@]}" "$@" 231 | # 232 | # but POSIX shell has neither arrays nor command substitution, so instead we 233 | # post-process each arg (as a line of input to sed) to backslash-escape any 234 | # character that might be a shell metacharacter, then use eval to reverse 235 | # that process (while maintaining the separation between arguments), and wrap 236 | # the whole thing up as a single "set" statement. 237 | # 238 | # This will of course break if any of these variables contains a newline or 239 | # an unmatched quote. 240 | # 241 | 242 | eval "set -- $( 243 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 244 | xargs -n1 | 245 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 246 | tr '\n' ' ' 247 | )" '"$@"' 248 | 249 | exec "$JAVACMD" "$@" 250 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%"=="" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%"=="" set DIRNAME=. 29 | @rem This is normally unused 30 | set APP_BASE_NAME=%~n0 31 | set APP_HOME=%DIRNAME% 32 | 33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 35 | 36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 38 | 39 | @rem Find java.exe 40 | if defined JAVA_HOME goto findJavaFromJavaHome 41 | 42 | set JAVA_EXE=java.exe 43 | %JAVA_EXE% -version >NUL 2>&1 44 | if %ERRORLEVEL% equ 0 goto execute 45 | 46 | echo. 47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 48 | echo. 49 | echo Please set the JAVA_HOME variable in your environment to match the 50 | echo location of your Java installation. 51 | 52 | goto fail 53 | 54 | :findJavaFromJavaHome 55 | set JAVA_HOME=%JAVA_HOME:"=% 56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 57 | 58 | if exist "%JAVA_EXE%" goto execute 59 | 60 | echo. 61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 62 | echo. 63 | echo Please set the JAVA_HOME variable in your environment to match the 64 | echo location of your Java installation. 65 | 66 | goto fail 67 | 68 | :execute 69 | @rem Setup the command line 70 | 71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 72 | 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if %ERRORLEVEL% equ 0 goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | set EXIT_CODE=%ERRORLEVEL% 85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 87 | exit /b %EXIT_CODE% 88 | 89 | :mainEnd 90 | if "%OS%"=="Windows_NT" endlocal 91 | 92 | :omega 93 | -------------------------------------------------------------------------------- /prompt-test.main.kts: -------------------------------------------------------------------------------- 1 | @file:DependsOn("cc.unitmesh:prompt-script:0.3.8") 2 | 3 | import cc.unitmesh.prompt.* 4 | 5 | executeScript("config/prompt.unit-mesh.yml") 6 | -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | rootProject.name = "devops-genius" 2 | -------------------------------------------------------------------------------- /src/main/kotlin/cc/unitmesh/genius/CiCdCommand.kt: -------------------------------------------------------------------------------- 1 | package cc.unitmesh.genius 2 | 3 | import com.github.ajalt.clikt.core.CliktCommand 4 | 5 | class CiCdCommand : CliktCommand(help = "Auto create CI/CD script with GenAI") { 6 | override fun run() { 7 | 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/main/kotlin/cc/unitmesh/genius/CodeReviewCommand.kt: -------------------------------------------------------------------------------- 1 | package cc.unitmesh.genius 2 | 3 | import cc.unitmesh.cf.code.GitCommand 4 | import cc.unitmesh.cf.code.GitDiffer 5 | import cc.unitmesh.genius.domain.review.CodeReviewAction 6 | import cc.unitmesh.genius.domain.review.ReviewOption 7 | import cc.unitmesh.genius.project.GeniusProject 8 | import com.github.ajalt.clikt.core.CliktCommand 9 | import com.github.ajalt.clikt.parameters.options.default 10 | import com.github.ajalt.clikt.parameters.options.flag 11 | import com.github.ajalt.clikt.parameters.options.option 12 | import org.slf4j.LoggerFactory 13 | import org.changelog.CommitParser 14 | import org.changelog.ParserOptions 15 | import com.charleskorn.kaml.PolymorphismStyle 16 | import com.charleskorn.kaml.Yaml 17 | import com.charleskorn.kaml.YamlConfiguration 18 | import kotlinx.serialization.serializer 19 | import java.io.File 20 | import kotlin.io.path.Path 21 | import kotlin.io.path.exists 22 | import kotlin.io.path.readText 23 | 24 | /** 25 | * The `CodeReviewCommand` class is a command-line interface for performing code reviews using AIGC (Artificial Intelligence for Code Review). 26 | * It extends the `CliktCommand` class from the `clikt` library and provides options and functionality for reviewing code in a Git repository. 27 | * 28 | * @property repo The path to the Git repository. It can be a local file path or a Git URL. Default value is "." (current directory). 29 | * @property branch The name of the Git branch to review. Default value is "master". 30 | * @property sinceCommit The revision of the first commit to include in the review. Default value is an empty string, which means the earliest commit in the repository. 31 | * @property untilCommit The revision of the last commit to include in the review. Default value is an empty string, which means the latest commit in the repository. 32 | * @property commitMessageOptionFile The path to the commit message option file. Default value is an empty string, which means no commit message options are used. 33 | * @property verbose A flag indicating whether to enable verbose output. Default value is false. 34 | * @property configFile The path to the configuration file. Default value is "devops-genius.yml". 35 | * @property project The `GeniusProject` instance representing the project being reviewed. It is lazily initialized based on the configuration file or the repository path. 36 | * 37 | * @constructor Creates a new instance of `CodeReviewCommand`. 38 | * 39 | * @see CliktCommand 40 | * @see GeniusProject 41 | */ 42 | class CodeReviewCommand : CliktCommand(help = "Code Review with AIGC") { 43 | private val repo by option(help = "Git repository path. Use local file path, or Git Url").default(".") 44 | private val branch by option(help = "Git branch name").default("master") 45 | private val sinceCommit by option(help = "Begin commit revision").default("") 46 | private val untilCommit by option(help = "End commit revision. Aka latest").default("") 47 | private val commitMessageOptionFile by option(help = "commit message option file").default("") 48 | private val verbose by option(help = "verbose").flag(default = false) 49 | private val configFile by option(help = "config file").default("devops-genius.yml") 50 | 51 | private val project: GeniusProject by lazy { 52 | val path = Path(configFile) 53 | if (path.exists()) { 54 | logger.info("load project from config file: ${path.toAbsolutePath()}") 55 | GeniusProject.fromYml(path.readText()) 56 | } else { 57 | logger.info("load project from repo: $repo") 58 | GeniusProject(path = repo) 59 | } 60 | } 61 | 62 | override fun run() { 63 | val defaultLatestIds = GitCommand().latestCommitHash(2).stdout.split(System.lineSeparator()) 64 | val sinceCommit = sinceCommit.ifEmpty { 65 | defaultLatestIds[defaultLatestIds.lastIndex] 66 | } 67 | val untilCommit = untilCommit.ifEmpty { 68 | defaultLatestIds[0] 69 | } 70 | 71 | val diff = GitDiffer(repo, branch) 72 | val repositoryUrl = diff.gitRepositoryUrl() 73 | logger.info("get repository url from .git/config: $repositoryUrl") 74 | project.repoUrl = repositoryUrl 75 | 76 | val reviewOption = ReviewOption( 77 | path = repo, 78 | repo = repositoryUrl, 79 | branch = branch, 80 | sinceCommit = sinceCommit, 81 | untilCommit = untilCommit, 82 | commitOptionFile = commitMessageOptionFile, 83 | verbose = verbose, 84 | project = project, 85 | ) 86 | 87 | val commitParser = createCommitParser() 88 | CodeReviewAction(project, reviewOption, diff, commitParser).execute() 89 | } 90 | 91 | private fun createCommitParser(): CommitParser { 92 | val parserOptions = if (commitMessageOptionFile.isNotEmpty() and Path(commitMessageOptionFile).exists()) { 93 | val commitMsgOptionText = File(commitMessageOptionFile).readText() 94 | ParserOptions.fromString(commitMsgOptionText) 95 | } else { 96 | ParserOptions.defaultOptions() 97 | } 98 | 99 | if (parserOptions == null) { 100 | throw Exception("commit message option file is not valid: $commitMessageOptionFile") 101 | } 102 | 103 | return CommitParser(parserOptions) 104 | } 105 | 106 | companion object { 107 | private val logger = LoggerFactory.getLogger(CodeReviewCommand::class.java)!! 108 | } 109 | } 110 | 111 | private fun ParserOptions.Companion.fromString(content: String): ParserOptions? { 112 | return try { 113 | val conf = YamlConfiguration(polymorphismStyle = PolymorphismStyle.Property) 114 | val userOptions = Yaml(configuration = conf).decodeFromString(serializer(), content) 115 | // merge default options 116 | defaultOptions().copy( 117 | commentChar = userOptions.commentChar ?: defaultOptions().commentChar, 118 | mergePattern = userOptions.mergePattern ?: defaultOptions().mergePattern, 119 | mergeCorrespondence = userOptions.mergeCorrespondence ?: defaultOptions().mergeCorrespondence, 120 | headerPattern = userOptions.headerPattern ?: defaultOptions().headerPattern, 121 | breakingHeaderPattern = userOptions.breakingHeaderPattern 122 | ?: defaultOptions().breakingHeaderPattern, 123 | headerCorrespondence = userOptions.headerCorrespondence ?: defaultOptions().headerCorrespondence, 124 | revertPattern = userOptions.revertPattern ?: defaultOptions().revertPattern, 125 | revertCorrespondence = userOptions.revertCorrespondence ?: defaultOptions().revertCorrespondence, 126 | fieldPattern = userOptions.fieldPattern ?: defaultOptions().fieldPattern, 127 | noteKeywords = userOptions.noteKeywords ?: defaultOptions().noteKeywords, 128 | notesPattern = userOptions.notesPattern ?: defaultOptions().notesPattern, 129 | issuePrefixes = userOptions.issuePrefixes ?: defaultOptions().issuePrefixes, 130 | issuePrefixesCaseSensitive = userOptions.issuePrefixesCaseSensitive 131 | ?: defaultOptions().issuePrefixesCaseSensitive, 132 | referenceActions = userOptions.referenceActions ?: defaultOptions().referenceActions, 133 | ) 134 | 135 | } catch (e: Exception) { 136 | e.printStackTrace() 137 | null 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /src/main/kotlin/cc/unitmesh/genius/IssueCommand.kt: -------------------------------------------------------------------------------- 1 | package cc.unitmesh.genius 2 | 3 | import com.github.ajalt.clikt.core.CliktCommand 4 | 5 | class IssueCommand : CliktCommand(help = "Resolve Issue with GenAI") { 6 | override fun run() { 7 | 8 | } 9 | 10 | } 11 | -------------------------------------------------------------------------------- /src/main/kotlin/cc/unitmesh/genius/Main.kt: -------------------------------------------------------------------------------- 1 | package cc.unitmesh.genius 2 | 3 | import com.github.ajalt.clikt.core.CliktCommand 4 | import com.github.ajalt.clikt.core.subcommands 5 | 6 | 7 | val HELP_TEXT = 8 | """GenAI/AIGC in DevOps practices that improve software development and operations through the integration.""".trimIndent() 9 | 10 | class GeniusCommand : CliktCommand(help = HELP_TEXT) { 11 | override fun run() = Unit 12 | } 13 | 14 | fun main(args: Array) = GeniusCommand().subcommands( 15 | CodeReviewCommand(), 16 | CiCdCommand(), 17 | IssueCommand(), 18 | ).main(args) 19 | -------------------------------------------------------------------------------- /src/main/kotlin/cc/unitmesh/genius/context/ActionOption.kt: -------------------------------------------------------------------------------- 1 | package cc.unitmesh.genius.context 2 | 3 | interface ActionOption { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /src/main/kotlin/cc/unitmesh/genius/context/GeniusAction.kt: -------------------------------------------------------------------------------- 1 | package cc.unitmesh.genius.context 2 | 3 | interface GeniusAction { 4 | fun execute(): Any 5 | } 6 | -------------------------------------------------------------------------------- /src/main/kotlin/cc/unitmesh/genius/devops/Issue.kt: -------------------------------------------------------------------------------- 1 | package cc.unitmesh.genius.devops 2 | 3 | import kotlinx.serialization.Serializable 4 | 5 | @Serializable 6 | data class Issue( 7 | val id: String, 8 | val title: String, 9 | val body: String, 10 | val url: String = "", 11 | val labels: List = listOf(), 12 | val assignees: List = listOf(), 13 | ) { 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/main/kotlin/cc/unitmesh/genius/devops/KanbanFactory.kt: -------------------------------------------------------------------------------- 1 | package cc.unitmesh.genius.devops 2 | 3 | import cc.unitmesh.genius.devops.kanban.GitHubKanban 4 | import cc.unitmesh.genius.devops.kanban.GitlabKanban 5 | import cc.unitmesh.genius.devops.kanban.Kanban 6 | import java.net.URL 7 | 8 | object KanbanFactory { 9 | 10 | /** 11 | * Creates a Kanban object based on the provided repository URL. 12 | * 13 | * @param url the URL of the repository 14 | * @return a Kanban object representing the repository's Kanban board, or null if the URL is invalid or unsupported 15 | */ 16 | fun fromRepositoryUrl(url: String, token: String): Kanban? { 17 | return try { 18 | val parsedUrl = URL(url) 19 | val host = parsedUrl.host 20 | 21 | when { 22 | host.contains("github.com") -> GitHubKanban(url, token) 23 | host.contains("gitlab.com") -> GitlabKanban(url, token) 24 | else -> GitlabKanban(url, token) 25 | } 26 | } catch (e: Exception) { 27 | null 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /src/main/kotlin/cc/unitmesh/genius/devops/kanban/GitHubKanban.kt: -------------------------------------------------------------------------------- 1 | package cc.unitmesh.genius.devops.kanban 2 | 3 | import cc.unitmesh.genius.devops.Issue 4 | import org.kohsuke.github.GitHub 5 | import org.kohsuke.github.GitHubBuilder 6 | 7 | class GitHubKanban(private val repoUrl: String, private val token: String) : Kanban { 8 | private val gitHub: GitHub 9 | 10 | init { 11 | try { 12 | gitHub = GitHubBuilder() 13 | .withOAuthToken(token) 14 | .build() 15 | } catch (e: Exception) { 16 | throw e 17 | } 18 | } 19 | 20 | override fun fetch(id: String): Issue { 21 | val repoUrl = formatUrl(this.repoUrl) 22 | val issue = gitHub.getRepository(repoUrl).getIssue(Integer.parseInt(id)) 23 | 24 | return Issue( 25 | issue.number.toString(), 26 | issue.title, 27 | issue.body, 28 | issue.url.toString(), 29 | issue.labels.map { it.name }, 30 | issue.assignees.map { it.name } 31 | ) 32 | } 33 | 34 | companion object { 35 | /** 36 | * Formats the repository URL to owner/repo format. 37 | * 38 | * The formatUrl method takes the repository URL and formats it to the owner/repo format. 39 | * For example, if the repository URL is "https://github.com/unitmesh/devti", 40 | * the formatted URL will be "unitmesh/devti". 41 | * 42 | * @return The formatted repository URL in the owner/repo format. 43 | */ 44 | fun formatUrl(repoUrl: String): String { 45 | var url = repoUrl.split("/").takeLast(2).joinToString("/") 46 | url = if (url.endsWith(".git")) url.substring(0, url.length - 4) else url 47 | return url 48 | } 49 | } 50 | } -------------------------------------------------------------------------------- /src/main/kotlin/cc/unitmesh/genius/devops/kanban/GitlabKanban.kt: -------------------------------------------------------------------------------- 1 | package cc.unitmesh.genius.devops.kanban 2 | 3 | import cc.unitmesh.genius.devops.Issue 4 | import org.gitlab4j.api.GitLabApi 5 | 6 | class GitlabKanban(val repoUrl: String, val token: String) : Kanban { 7 | private var gitLabApi: GitLabApi = GitLabApi(repoUrl, token) 8 | override fun fetch(id: String): Issue { 9 | val issue: org.gitlab4j.api.models.Issue = gitLabApi.issuesApi.getIssue(repoUrl, id.toLong()) 10 | return Issue( 11 | issue.iid.toString(), 12 | issue.title, 13 | issue.description, 14 | issue.webUrl, 15 | issue.labels, 16 | issue.assignees.map { it.name } 17 | ) 18 | } 19 | 20 | } -------------------------------------------------------------------------------- /src/main/kotlin/cc/unitmesh/genius/devops/kanban/Kanban.kt: -------------------------------------------------------------------------------- 1 | package cc.unitmesh.genius.devops.kanban 2 | 3 | import cc.unitmesh.genius.devops.Issue 4 | 5 | interface Kanban { 6 | fun fetch(id: String): Issue 7 | } 8 | -------------------------------------------------------------------------------- /src/main/kotlin/cc/unitmesh/genius/domain/cicd/CiFileGenerator.kt: -------------------------------------------------------------------------------- 1 | package cc.unitmesh.genius.domain.cicd 2 | 3 | import java.io.File 4 | 5 | abstract class CiFileGenerator { 6 | protected fun rootDir(): File { 7 | return File("").absoluteFile 8 | } 9 | 10 | abstract fun createFile() 11 | } -------------------------------------------------------------------------------- /src/main/kotlin/cc/unitmesh/genius/domain/cicd/GitHubActionPromptFactory.kt: -------------------------------------------------------------------------------- 1 | package cc.unitmesh.genius.domain.cicd 2 | 3 | import cc.unitmesh.genius.prompt.PromptFactory 4 | 5 | class GitHubActionPromptFactory : PromptFactory("prompts/cicd/github-action") { 6 | } -------------------------------------------------------------------------------- /src/main/kotlin/cc/unitmesh/genius/domain/review/CodeReviewAction.kt: -------------------------------------------------------------------------------- 1 | package cc.unitmesh.genius.domain.review 2 | 3 | import cc.unitmesh.cf.code.GitDiffer 4 | import cc.unitmesh.genius.context.GeniusAction 5 | import cc.unitmesh.genius.devops.Issue 6 | import cc.unitmesh.genius.project.GeniusProject 7 | import org.changelog.CommitParser 8 | import org.slf4j.LoggerFactory 9 | 10 | class CodeReviewAction( 11 | val project: GeniusProject, 12 | private val option: ReviewOption, 13 | private val diff: GitDiffer, 14 | private val commitParser: CommitParser, 15 | ) : GeniusAction { 16 | private val promptFactory = CodeReviewPromptFactory() 17 | private val context = CodeReviewContext() 18 | 19 | /** 20 | * Executes the full process of reviewing commits. 21 | * 22 | * This method performs the following steps: 23 | * 1. Retrieves the commit messages between the specified `sinceCommit` and `untilCommit`. 24 | * 2. Parses the commit messages to obtain the Commit objects and their references. 25 | * 3. Filters out the commits that do not require review based on the project's configuration. 26 | * 4. Retrieves the titles of the User Stories associated with the referenced issues. 27 | * 5. Generates a patch file to obtain the diff content and filters out files that do not require review. 28 | * 6. Creates a prompt to collect the review results. 29 | * 7. Sends the prompt to the project's connector for completion. 30 | * 8. Prints the completion results. 31 | * 32 | * @return An empty string. 33 | */ 34 | override fun execute(): Any { 35 | // 获取 sinceCommit 到 untilCommit 之间的 commit message 36 | val commitMessages = diff.commitMessagesBetween(option.sinceCommit, option.untilCommit) 37 | context.fullMessage = commitMessages.map { it.value }.joinToString(System.lineSeparator()) 38 | 39 | // 解析 commit message 为 Commit 对象,以获取其中的 references 40 | val parsedMsgs = commitMessages.map { 41 | commitParser.parse(it.value) 42 | } 43 | 44 | // 从配置文件中读取,并过滤掉不需要 review 的 commit,诸如 chore、ci, docs 等 45 | // 如果没有配置,则全部需要 review 46 | val filterCommits = parsedMsgs.filter { 47 | if (it.meta.containsKey("type")) { 48 | val type = it.meta["type"] as String 49 | project.commitLog?.isIgnoreType(type) ?: true 50 | } else { 51 | true 52 | } 53 | } 54 | 55 | if (option.verbose) { 56 | println("parsedMsgs: $parsedMsgs") 57 | println("filterCommits: $filterCommits") 58 | } 59 | 60 | if (filterCommits.isEmpty()) { 61 | logger.info("commit don't need review") 62 | } 63 | 64 | // 获取所有的 issue id,以获取对应的 User Story 的标题信息,作为业务的上下文使用 65 | val storyIds = parsedMsgs.map { it.references }.flatten() 66 | val stories = storyIds.map { 67 | try { 68 | project.fetchStory(it.issue) 69 | } catch (e: Exception) { 70 | logger.error("fetch story error: $it", e) 71 | null 72 | } 73 | }.filterNotNull() 74 | 75 | context.businessContext = stories.joinToString(System.lineSeparator(), transform = Issue::title) 76 | 77 | // 生成 patch 文件,以获取 diff 的内容,并过滤掉不需要 review 的文件,诸如 .json、.yaml 等 78 | val patch = diff.patchBetween(option.sinceCommit, option.untilCommit) 79 | context.changes = patch.filter { 80 | project.commitLog?.isIgnoreFile(it.key) ?: true 81 | }.map { 82 | it.value.content 83 | }.joinToString(System.lineSeparator()) 84 | 85 | // 生成 prompt,以获取 review 的结果 86 | promptFactory.context = context 87 | val messages = promptFactory.createPrompt(project, "") 88 | 89 | logger.info("messages: $messages") 90 | 91 | project.connector().streamCompletion(messages).blockingForEach { 92 | print(it) 93 | } 94 | 95 | return "" 96 | } 97 | 98 | companion object { 99 | val logger = LoggerFactory.getLogger(CodeReviewAction::class.java) 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/main/kotlin/cc/unitmesh/genius/domain/review/CodeReviewContext.kt: -------------------------------------------------------------------------------- 1 | package cc.unitmesh.genius.domain.review 2 | 3 | data class CodeReviewContext( 4 | var businessContext: String = "", 5 | var fullMessage: String = "", 6 | var changes: String = "", 7 | ) -------------------------------------------------------------------------------- /src/main/kotlin/cc/unitmesh/genius/domain/review/CodeReviewPromptFactory.kt: -------------------------------------------------------------------------------- 1 | package cc.unitmesh.genius.domain.review 2 | 3 | import cc.unitmesh.genius.prompt.PromptFactory 4 | 5 | class CodeReviewPromptFactory(public override var context: CodeReviewContext = CodeReviewContext()) : 6 | PromptFactory("code-review") { 7 | override val templatePath = "simple-review.open-ai.vm" 8 | } 9 | -------------------------------------------------------------------------------- /src/main/kotlin/cc/unitmesh/genius/domain/review/ReviewOption.kt: -------------------------------------------------------------------------------- 1 | package cc.unitmesh.genius.domain.review 2 | 3 | import cc.unitmesh.genius.context.ActionOption 4 | import cc.unitmesh.genius.project.GeniusProject 5 | 6 | class ReviewOption( 7 | val path: String = "", 8 | val verbose: Boolean, 9 | val repo: String, 10 | val branch: String, 11 | val sinceCommit: String, 12 | val untilCommit: String, 13 | val commitOptionFile: String, 14 | val project: GeniusProject, 15 | ) : ActionOption { 16 | 17 | } -------------------------------------------------------------------------------- /src/main/kotlin/cc/unitmesh/genius/project/BuildSystemType.java: -------------------------------------------------------------------------------- 1 | package cc.unitmesh.genius.project; 2 | 3 | public enum BuildSystemType { 4 | GRADLE, 5 | MAVEN, 6 | YARN, 7 | NPM, 8 | PYTHON, 9 | UNKNOWN; 10 | } 11 | 12 | -------------------------------------------------------------------------------- /src/main/kotlin/cc/unitmesh/genius/project/BuildSystemTypeDescriptor.kt: -------------------------------------------------------------------------------- 1 | package cc.unitmesh.genius.project 2 | 3 | class BuildSystemTypeDescriptor { 4 | fun getBuildSystemType(project: GeniusProject): BuildSystemType { 5 | TODO() 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/main/kotlin/cc/unitmesh/genius/project/GeniusProject.kt: -------------------------------------------------------------------------------- 1 | package cc.unitmesh.genius.project 2 | 3 | import cc.unitmesh.cf.core.llms.LlmProvider 4 | import cc.unitmesh.cf.core.llms.MockLlmProvider 5 | import cc.unitmesh.connection.ConnectionConfig 6 | import cc.unitmesh.connection.MockLlmConnection 7 | import cc.unitmesh.connection.OpenAiConnection 8 | import cc.unitmesh.genius.devops.Issue 9 | import cc.unitmesh.genius.devops.KanbanFactory 10 | import cc.unitmesh.openai.OpenAiProvider 11 | import com.charleskorn.kaml.PolymorphismStyle 12 | import com.charleskorn.kaml.Yaml 13 | import com.charleskorn.kaml.YamlConfiguration 14 | import kotlinx.serialization.Serializable 15 | import kotlinx.serialization.Transient 16 | import kotlinx.serialization.decodeFromString 17 | import java.io.File 18 | import java.nio.file.FileSystems 19 | import java.nio.file.Path 20 | 21 | @Serializable 22 | data class GeniusProject( 23 | val name: String = "", 24 | val path: String = "", 25 | val store: GeniusStore? = null, 26 | val kanban: GeniusKanban? = null, 27 | val commitLog: GeniusCommitLog? = null, 28 | val connection: String = "connection.yml", 29 | ) { 30 | var repoUrl: String = "" 31 | 32 | fun fetchStory(id: String): Issue { 33 | return KanbanFactory.fromRepositoryUrl(repoUrl, kanban!!.token)!!.fetch(id) 34 | } 35 | 36 | fun connector(): LlmProvider { 37 | val text = File(connection).readBytes().toString(Charsets.UTF_8) 38 | val configuration = YamlConfiguration(polymorphismStyle = PolymorphismStyle.Property) 39 | val connection = Yaml(configuration = configuration).decodeFromString(text).convert() 40 | 41 | val llmProvider = when (connection) { 42 | is OpenAiConnection -> OpenAiProvider(connection.apiKey, connection.apiHost) 43 | is MockLlmConnection -> MockLlmProvider(connection.response) 44 | else -> throw Exception("unsupported connection type: ${connection.type}") 45 | } 46 | 47 | return llmProvider 48 | } 49 | 50 | 51 | companion object { 52 | fun fromYml(yaml: String): GeniusProject { 53 | val conf = YamlConfiguration(polymorphismStyle = PolymorphismStyle.Property) 54 | return Yaml(configuration = conf).decodeFromString(serializer(), yaml) 55 | } 56 | } 57 | } 58 | 59 | /** 60 | * Vector store of a project will be used to search project code and documents. 61 | */ 62 | @Serializable 63 | data class GeniusStore( 64 | val indexName: String = "", 65 | ) 66 | 67 | /** 68 | * Kanban configuration of project, will be used to fetch issues from kanban board. 69 | */ 70 | @Serializable 71 | data class GeniusKanban( 72 | val url: String = "", 73 | val token: String = "", 74 | val type: String = "", 75 | ) 76 | 77 | @Serializable 78 | data class GeniusCommitLog( 79 | val ignoreType: List, 80 | /** 81 | * Ignore files when generate commit log, which is a list of glob pattern. 82 | */ 83 | val ignorePatterns: List, 84 | ) { 85 | @Transient 86 | private val compiledPatterns = ignorePatterns.map { 87 | FileSystems.getDefault().getPathMatcher("glob:$it") 88 | } 89 | 90 | fun isIgnoreType(type: String): Boolean { 91 | return !ignoreType.contains(type) 92 | } 93 | 94 | fun isIgnoreFile(filename: String): Boolean { 95 | return compiledPatterns.none { it.matches(Path.of(filename)) } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/main/kotlin/cc/unitmesh/genius/prompt/PromptBuilder.kt: -------------------------------------------------------------------------------- 1 | package cc.unitmesh.genius.prompt 2 | 3 | interface PromptBuilder { 4 | 5 | } -------------------------------------------------------------------------------- /src/main/kotlin/cc/unitmesh/genius/prompt/PromptFactory.kt: -------------------------------------------------------------------------------- 1 | package cc.unitmesh.genius.prompt 2 | 3 | import cc.unitmesh.cf.core.llms.LlmMsg 4 | import cc.unitmesh.genius.project.GeniusProject 5 | import cc.unitmesh.template.TemplateRoleSplitter 6 | import cc.unitmesh.template.VelocityCompiler 7 | 8 | abstract class PromptFactory(promptsBasePath: String) { 9 | protected val template = VelocityCompiler() 10 | protected val promptsLoader: PromptsLoader 11 | protected val splitter = TemplateRoleSplitter() 12 | protected open val context: Any = "" 13 | 14 | open val templatePath = "" 15 | 16 | init { 17 | promptsLoader = PromptsLoader(promptsBasePath) 18 | } 19 | 20 | open fun createPrompt(project: GeniusProject, description: String): List { 21 | val prompt = promptsLoader.getTemplate(templatePath) 22 | 23 | val msgs = splitter.split(prompt) 24 | val messages = LlmMsg.fromMap(msgs).toMutableList() 25 | 26 | messages.map { 27 | if (it.role == LlmMsg.ChatRole.User) { 28 | template.append("context", context) 29 | it.content = template.compileToString(it.content) 30 | } 31 | } 32 | 33 | return messages 34 | } 35 | } -------------------------------------------------------------------------------- /src/main/kotlin/cc/unitmesh/genius/prompt/PromptsLoader.kt: -------------------------------------------------------------------------------- 1 | package cc.unitmesh.genius.prompt 2 | 3 | import java.nio.charset.Charset 4 | 5 | class PromptsLoader(prefix: String) { 6 | private val classLoader: ClassLoader = this.javaClass.classLoader 7 | private val defaultPrefix: String = prefix.trimEnd('/') 8 | 9 | fun getTemplate(path: String): String { 10 | val resourceUrl = classLoader.getResource("$defaultPrefix/$path") ?: throw PromptNotFoundError(path) 11 | val bytes = resourceUrl.readBytes() 12 | return String(bytes, Charset.forName("UTF-8")) 13 | } 14 | } 15 | 16 | class PromptNotFoundError(path: String) : Exception("Prompt not found at path: $path") 17 | -------------------------------------------------------------------------------- /src/main/resources/code-review/simple-review.open-ai.vm: -------------------------------------------------------------------------------- 1 | ```user``` 2 | You are a senior software developer, who can help me do code review a commit. 3 | 4 | Use the following response format, keeping the section headings as-is, and provide 5 | your feedback. Use bullet points for each response. The provided examples are for 6 | illustration purposes only and should not be repeated. 7 | 8 | **Syntax and logical errors (example)**: 9 | - Incorrect indentation on line 12 10 | - Missing closing parenthesis on line 23 11 | 12 | **Code refactoring and quality (example)**: 13 | - Replace multiple if-else statements with a switch case for readability 14 | - Extract repetitive code into separate functions 15 | 16 | **Performance optimization (example)**: 17 | - Use a more efficient sorting algorithm to reduce time complexity 18 | - Cache results of expensive operations for reuse 19 | 20 | **Security vulnerabilities (example)**: 21 | - Sanitize user input to prevent SQL injection attacks 22 | - Use prepared statements for database queries 23 | 24 | **Best practices (example)**: 25 | - Add meaningful comments and documentation to explain the code 26 | - Follow consistent naming conventions for variables and functions 27 | 28 | Business Context: ${context.businessContext} 29 | 30 | Commit Message: ${context.fullMessage} 31 | 32 | Code Changes: ${context.changes} 33 | 34 | 作为您的 Tech Lead,我只关注一些关键的代码审查问题。请为我提供一个关键摘要,按照以下格式: 35 | 36 | 关键摘要: // 你应该使用中文来回答,合并相似的问题 37 | 是否建议立即修改: // 是/否 38 | -------------------------------------------------------------------------------- /src/main/resources/container/docker/docker-file.vm: -------------------------------------------------------------------------------- 1 | Please write a Dockerfile with minimal steps. 2 | 3 | - I need to build the application with Gradle and run with JDK 11. 4 | - I need the building to be done in separate base image than running the build. 5 | - I need the application port to be 3000. 6 | 7 | Output only the Dockerfile content without any explanation. 8 | -------------------------------------------------------------------------------- /src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | UTF-8 5 | 6 | [SCANNER] %logger{36} %msg%n 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/main/resources/prompts/cicd/github/common/github_actions_events.prompt: -------------------------------------------------------------------------------- 1 | Description: 2 | Create build.yml YAML file for GitHub Action which triggers on push. 3 | Code: 4 | on: 5 | push 6 | Description: 7 | Create build.yml YAML file for GitHub Action which triggers on push and pull request. 8 | Code: 9 | on: 10 | push: 11 | pull_request: 12 | Description: 13 | Create build.yml YAML file for GitHub Action which triggers on push to branches 'main', all 'releases'. 14 | Code: 15 | on: 16 | push: 17 | branches: 18 | - main 19 | - releases/** 20 | Description: 21 | Create build.yml YAML file for GitHub Action which triggers on opened or closed pull request to branches 'main'. 22 | Code: 23 | on: 24 | pull_request: 25 | branches: 26 | - main 27 | types: 28 | - opened 29 | - closed 30 | Description: 31 | Create build.yml YAML file for GitHub Action which triggers on push to any branch if any kotlin file change. 32 | Code: 33 | on: 34 | push: 35 | paths: 36 | - '**.kt' 37 | Description: 38 | Create build.yml YAML file for GitHub Action which triggers at 8am every day. 39 | Code: 40 | on: 41 | schedule: 42 | - cron: '0 8 * * *' -------------------------------------------------------------------------------- /src/main/resources/prompts/cicd/github/common/github_actions_publish_docker.prompt: -------------------------------------------------------------------------------- 1 | Description: 2 | Create build.yml YAML file for GitHub Action. 3 | Define image namespace in variable DOCKER_HUB_NAMESPACE as jetbrains. 4 | Define image repository name in variable DOCKER_HUB_REPOSITORY as hackathon. 5 | Use latest ubuntu version. 6 | Checkout source code. 7 | Log in to Docker Hub using secret DOCKER_USERNAME as username and DOCKER_PASSWORD as password. 8 | Extract metadata for image. 9 | Build and push Docker. 10 | Use docker file from project root. 11 | Use information about tags and labels from docker metadata from previous step. 12 | Code: 13 | env: 14 | DOCKER_HUB_NAMESPACE: jetbrains 15 | DOCKER_HUB_REPOSITORY: hackathon 16 | jobs: 17 | push_to_registry: 18 | name: Push Docker image to Docker Hub 19 | runs-on: ubuntu-latest 20 | steps: 21 | - name: Check out the repo 22 | uses: actions/checkout@v3 23 | 24 | - name: Log in to Docker Hub 25 | uses: docker/login-action@v2 26 | with: 27 | username: ${{ secrets.DOCKER_USERNAME }} 28 | password: ${{ secrets.DOCKER_PASSWORD }} 29 | 30 | - name: Extract metadata (tags, labels) for Docker 31 | id: meta 32 | uses: docker/metadata-action@v4 33 | with: 34 | images: ${{ env.DOCKER_HUB_NAMESPACE }}/${{ env.DOCKER_HUB_REPOSITORY }} 35 | 36 | - name: Build and push Docker image 37 | uses: docker/build-push-action@v4 38 | with: 39 | context: . 40 | push: true 41 | tags: ${{ steps.meta.outputs.tags }} 42 | labels: ${{ steps.meta.outputs.labels }} 43 | Description: 44 | Create build.yml YAML file for GitHub Action which on latest ubuntu version push Docker image which name as repository has to GitHub Docker Registry using docker file from project root 45 | Code: 46 | env: 47 | REGISTRY: ghcr.io 48 | IMAGE_NAME: ${{ github.repository }} 49 | 50 | jobs: 51 | build-and-push-image: 52 | runs-on: ubuntu-latest 53 | permissions: 54 | contents: read 55 | packages: write 56 | 57 | steps: 58 | - name: Checkout repository 59 | uses: actions/checkout@v3 60 | 61 | - name: Log in to the Container registry 62 | uses: docker/login-action@v2 63 | with: 64 | registry: ${{ env.REGISTRY }} 65 | username: ${{ github.actor }} 66 | password: ${{ secrets.GITHUB_TOKEN }} 67 | 68 | - name: Extract metadata (tags, labels) for Docker 69 | id: meta 70 | uses: docker/metadata-action@v3 71 | with: 72 | images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} 73 | 74 | - name: Build and push Docker image 75 | uses: docker/build-push-action@v4 76 | with: 77 | context: . 78 | push: true 79 | tags: ${{ steps.meta.outputs.tags }} 80 | labels: ${{ steps.meta.outputs.labels }} -------------------------------------------------------------------------------- /src/main/resources/prompts/cicd/github/gradle/github_actions_build_gradle.prompt: -------------------------------------------------------------------------------- 1 | Description: 2 | Create build.yml YAML file for GitHub Action for java version 17 gradle project which use latest ubuntu version builds project and runs tests. 3 | Code: 4 | on: 5 | push 6 | jobs: 7 | build: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v3 11 | - name: Set up JDK 17 12 | uses: actions/setup-java@v3 13 | with: 14 | java-version: '17' 15 | - name: Validate Gradle wrapper 16 | uses: gradle/wrapper-validation-action@v2 17 | - name: Build with Gradle 18 | uses: gradle/gradle-build-action@v2 19 | with: 20 | arguments: build -------------------------------------------------------------------------------- /src/main/resources/prompts/cicd/github/gradle/github_actions_publish_gradle.prompt: -------------------------------------------------------------------------------- 1 | Description: 2 | Create build.yml YAML file for GitHub Action. 3 | Project build system is 'gradle'. 4 | Setup java version '11', distribution 'adopt' 5 | Build project. 6 | Publish package to maven repository. 7 | Take MAVEN_USERNAME env variable from OSSRH_USERNAME secret 8 | Take MAVEN_PASSWORD env variable from OSSRH_TOKEN secret 9 | Code: 10 | jobs: 11 | publish: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v3 15 | - name: Set up Java 16 | uses: actions/setup-java@v3 17 | with: 18 | java-version: '11' 19 | distribution: 'adopt' 20 | - name: Validate Gradle wrapper 21 | uses: gradle/wrapper-validation-action@v1 22 | - name: Publish package 23 | uses: gradle/gradle-build-action@v2 24 | with: 25 | arguments: publish 26 | env: 27 | MAVEN_USERNAME: ${{ secrets.OSSRH_USERNAME }} 28 | MAVEN_PASSWORD: ${{ secrets.OSSRH_TOKEN }} -------------------------------------------------------------------------------- /src/main/resources/prompts/cicd/github/maven/github_actions_build_maven.prompt: -------------------------------------------------------------------------------- 1 | Description: 2 | Create build.yml YAML file for GitHub Action for java version 17 maven project which on latest ubuntu version builds project and runs tests 3 | Code: 4 | jobs: 5 | build: 6 | runs-on: ubuntu-latest 7 | steps: 8 | - uses: actions/checkout@v3 9 | - name: Set up JDK 17 10 | uses: actions/setup-java@v3 11 | with: 12 | java-version: '17' 13 | - name: Build with Maven 14 | run: mvn --batch-mode --update-snapshots package -------------------------------------------------------------------------------- /src/main/resources/prompts/cicd/github/maven/github_actions_publish_maven.prompt: -------------------------------------------------------------------------------- 1 | Description: 2 | Create build.yml YAML file for GitHub Action for java gradle project publish package to maven repository 3 | Code: 4 | jobs: 5 | publish: 6 | runs-on: ubuntu-latest 7 | steps: 8 | - uses: actions/checkout@v3 9 | - name: Set up Maven Central Repository 10 | uses: actions/setup-java@v3 11 | with: 12 | java-version: '11' 13 | server-id: ossrh 14 | server-username: MAVEN_USERNAME 15 | server-password: MAVEN_PASSWORD 16 | - name: Publish package 17 | run: mvn --batch-mode deploy 18 | env: 19 | MAVEN_USERNAME: ${{ secrets.OSSRH_USERNAME }} 20 | MAVEN_PASSWORD: ${{ secrets.OSSRH_TOKEN }} -------------------------------------------------------------------------------- /src/main/resources/prompts/cicd/jenkins/gradle/jenkins_build_gradle.prompt: -------------------------------------------------------------------------------- 1 | Description: 2 | Create Jenkinsfile for Jenkins for java version 17 gradle project which use latest ubuntu version builds project and runs tests. 3 | Code: 4 | node { 5 | agent { 6 | label 'ubuntu' 7 | } 8 | 9 | tools { 10 | jdk 'jdk_17_latest' 11 | } 12 | 13 | stage 'Stage Checkout' 14 | 15 | checkout scm 16 | sh 'git submodule update --init' 17 | 18 | stage 'Stage Build' 19 | sh './gradlew build' 20 | } 21 | -------------------------------------------------------------------------------- /src/test/kotlin/cc/unitmesh/genius/devops/KanbanFactoryTest.kt: -------------------------------------------------------------------------------- 1 | package cc.unitmesh.genius.devops; 2 | 3 | import cc.unitmesh.genius.devops.kanban.GitHubKanban 4 | import cc.unitmesh.genius.devops.kanban.GitlabKanban 5 | import io.kotest.matchers.shouldBe 6 | import org.junit.jupiter.api.Test 7 | 8 | class KanbanServiceTest { 9 | 10 | @Test 11 | fun should_return_GitHubKanban_when_given_valid_github_url() { 12 | // given 13 | val url = "https://github.com/example/repository" 14 | 15 | // when 16 | val result = KanbanFactory.fromRepositoryUrl(url, "token") 17 | 18 | // then 19 | result!!.javaClass shouldBe GitHubKanban::class.java 20 | } 21 | 22 | @Test 23 | fun should_return_GitlabKanban_when_given_valid_gitlab_url() { 24 | // given 25 | val url = "https://gitlab.com/example/repository" 26 | 27 | // when 28 | val result = KanbanFactory.fromRepositoryUrl(url, "token") 29 | 30 | // then 31 | result!!.javaClass shouldBe GitlabKanban::class.java 32 | } 33 | } -------------------------------------------------------------------------------- /src/test/kotlin/cc/unitmesh/genius/devops/kanban/GitHubKanbanTest.kt: -------------------------------------------------------------------------------- 1 | package cc.unitmesh.genius.devops.kanban; 2 | 3 | import org.junit.jupiter.api.Test 4 | import org.junit.jupiter.api.Assertions.assertEquals 5 | 6 | class GitHubKanbanTest { 7 | 8 | @Test 9 | fun should_formatUrl_correctly() { 10 | // given 11 | val repoUrl = "https://github.com/unitmesh/devti" 12 | val expectedFormattedUrl = "unitmesh/devti" 13 | 14 | // when 15 | val formattedUrl = GitHubKanban.formatUrl(repoUrl) 16 | 17 | // then 18 | assertEquals(expectedFormattedUrl, formattedUrl) 19 | } 20 | } -------------------------------------------------------------------------------- /src/test/kotlin/cc/unitmesh/genius/domain/review/CodeReviewPromptFactoryTest.kt: -------------------------------------------------------------------------------- 1 | package cc.unitmesh.genius.domain.review; 2 | 3 | import cc.unitmesh.genius.project.GeniusProject 4 | import io.kotest.matchers.shouldNotBe 5 | import org.junit.jupiter.api.Test; 6 | 7 | class CodeReviewPromptFactoryTest { 8 | @Test 9 | fun should_success_create_prompt() { 10 | val codeReviewPromptFactory = CodeReviewPromptFactory() 11 | val prompt = codeReviewPromptFactory.createPrompt(GeniusProject(), "test") 12 | prompt shouldNotBe null 13 | } 14 | } -------------------------------------------------------------------------------- /src/test/kotlin/cc/unitmesh/genius/project/GeniusProjectTest.kt: -------------------------------------------------------------------------------- 1 | package cc.unitmesh.genius.project; 2 | 3 | import io.kotest.matchers.shouldBe 4 | import org.junit.jupiter.api.Test 5 | 6 | class GeniusProjectTest { 7 | 8 | @Test 9 | fun `fromYml should return GeniusProject object`() { 10 | // given 11 | val yaml = """ 12 | name: "ChocolateFactory" 13 | path: "." # relative to the project root, or GitHub repo, like "unitmesh/chocolate-factory" 14 | 15 | # store the changelog in the repo 16 | store: 17 | indexName: "unitmesh/chocolate-factory" # default to github repo 18 | 19 | kanban: 20 | type: GitHub 21 | token: "xx" 22 | 23 | commitLog: 24 | ignoreType: [ "chore", "docs", "style" ] 25 | ignorePatterns: ["*.md", "*.json"] 26 | """.trimIndent() 27 | 28 | // when 29 | val geniusProject = GeniusProject.fromYml(yaml) 30 | 31 | // then 32 | geniusProject.name shouldBe "ChocolateFactory" 33 | geniusProject.path shouldBe "." 34 | } 35 | } --------------------------------------------------------------------------------