├── .adr.json ├── .editorconfig ├── .gitattributes ├── .github ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── workflows │ ├── build.yml │ ├── coverage.yml │ └── release.yml ├── .gitignore ├── .npmignore ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── Cargo.lock ├── Cargo.toml ├── DEVELOPMENT.md ├── LICENSE ├── README.md ├── _config.yml ├── _fixtures ├── config.yml ├── ctags │ ├── coco_class_tags │ ├── coco_tags │ ├── cpp_tags │ ├── go_tags │ ├── java_tags │ ├── main.go │ ├── source │ │ ├── TypeName.java │ │ ├── animal.ts │ │ ├── datastore.go │ │ └── field.cpp │ └── ts_tags ├── dockerfile │ └── Go.Dockerfile ├── pipeline │ └── jenkinsfile │ │ └── Jenkinsfile ├── projects │ ├── go │ │ ├── hello │ │ │ └── main.go │ │ └── simple │ │ │ └── go.mod │ ├── java │ │ ├── hello │ │ │ ├── HelloWorld.java │ │ │ └── pom.xml │ │ ├── multi_mod_maven_project │ │ │ ├── module1 │ │ │ │ ├── pom.xml │ │ │ │ └── src │ │ │ │ │ ├── main │ │ │ │ │ ├── java │ │ │ │ │ │ └── Hello.java │ │ │ │ │ └── resources │ │ │ │ │ │ └── application.yml │ │ │ │ │ └── test │ │ │ │ │ ├── java │ │ │ │ │ └── HelloTest.java │ │ │ │ │ └── resources │ │ │ │ │ └── application.yml │ │ │ ├── module2 │ │ │ │ ├── pom.xml │ │ │ │ └── src │ │ │ │ │ ├── main │ │ │ │ │ ├── java │ │ │ │ │ │ └── Hello.java │ │ │ │ │ └── resources │ │ │ │ │ │ └── application.yml │ │ │ │ │ └── test │ │ │ │ │ ├── java │ │ │ │ │ └── HelloTest.java │ │ │ │ │ └── resources │ │ │ │ │ └── application.yml │ │ │ ├── pom.xml │ │ │ └── src │ │ │ │ ├── main │ │ │ │ ├── java │ │ │ │ │ └── Hello.java │ │ │ │ └── resources │ │ │ │ │ └── application.yml │ │ │ │ └── test │ │ │ │ ├── java │ │ │ │ └── HelloTest.java │ │ │ │ └── resources │ │ │ │ └── application.yml │ │ └── simple │ │ │ ├── .gitignore │ │ │ ├── app │ │ │ ├── HelloWorld.java │ │ │ └── pom.xml │ │ │ ├── build.gradle │ │ │ ├── ignore.js │ │ │ └── settings.gradle │ ├── js │ │ ├── bowerproject │ │ │ ├── bower.json │ │ │ └── bower_components │ │ │ │ └── .gitkeep │ │ └── npmproject │ │ │ ├── node_modules │ │ │ └── .gitkeep │ │ │ └── package.json │ ├── jvm │ │ ├── Hello.groovy │ │ ├── Hello.kt │ │ ├── Hello.scala │ │ ├── build.gradle │ │ ├── mavenproject │ │ │ ├── Hello.kt │ │ │ ├── HelloWorldTest.java │ │ │ ├── Helloworld.java │ │ │ └── pom.xml │ │ └── settings.gradle │ └── rust │ │ └── cargo │ │ └── Cargo.toml ├── repos │ └── root │ │ ├── app1 │ │ └── .gittest │ │ └── app2 │ │ └── .gittest ├── swagger │ └── petstore.yaml └── tech_mapping.yml ├── coco.yml ├── core_model ├── Cargo.toml └── src │ ├── coco_config.rs │ ├── coco_settings.rs │ ├── coco_struct.rs │ ├── lib.rs │ ├── plugin_interface.rs │ └── support │ ├── mod.rs │ ├── path_format.rs │ └── url_format.rs ├── docs ├── README.md ├── adr │ ├── 0001-metrics-model-design.md │ ├── 0002-chart-library.md │ ├── 0003-config-design.md │ ├── 0004-format-git-commit-message.md │ ├── 0005-embedded-database.md │ ├── 0006-tech-stack-generate.md │ ├── 0007-regex-match-commit-message-design.md │ ├── 0008-virualenv-for-runtime.md │ ├── 0009-git-http-server.md │ ├── 0010-limit-git-changes-by-time.md │ ├── 0011-architecture-analysis.md │ ├── 0012-file-change-count.md │ ├── 0013-suggest-design.md │ ├── 0014-organization-description-languages.md │ ├── 0015-struct-analysis.md │ ├── 0016-plugin-system-design.md │ ├── 0017-usr-cargo-make-for-custom-tasks-sets.md │ ├── 0018-cocomo-analysis-plugin.md │ ├── 0019-read-code-coverage.md │ ├── 0020-output-for-plugins.md │ ├── 0021-plugin-config.md │ ├── 0022-suggest-with-i18n.md │ ├── 0023-psa(project-structure-analysis).md │ ├── 0024-jenkinsfile-parser.md │ ├── 0025-deploy-analyser.md │ ├── 0026-use-plantuml-for-convert-uml.md │ ├── 0027-dockefile-analysis.md │ ├── 0028-kubernetes-analysis.md │ ├── 0029-integration-openapi-source-code.md │ ├── 0030-extract-infrastructure-mod.md │ ├── 0031-markdown-for-terminal-output.md │ └── README.md ├── case-study │ └── README.md ├── coco_sample.yml ├── commands.md ├── images │ ├── coco-architecture.svg │ ├── jetbrains.svg │ └── psa.svg ├── model.md ├── related.md ├── social │ └── coco-intro.md └── thesis │ ├── arch-mapping.md │ ├── gitana.md │ └── team-first.md ├── e2e ├── .gitignore ├── Cargo.toml ├── _fixtures │ ├── coco-fixtures.yml │ └── no-plugin.yml └── src │ └── lib.rs ├── framework ├── Cargo.toml ├── README.md └── src │ ├── dependency │ ├── base_library.rs │ ├── library_dependency.rs │ ├── mod.rs │ ├── module_dependency.rs │ └── project_dependency.rs │ ├── facet │ ├── go │ │ ├── go_facet.rs │ │ └── mod.rs │ ├── java │ │ ├── content_root.rs │ │ ├── java_facet.rs │ │ ├── java_module_data.rs │ │ ├── jvm_facet.rs │ │ └── mod.rs │ ├── javascript │ │ ├── javascript_facet.rs │ │ └── mod.rs │ ├── mod.rs │ ├── python │ │ ├── mod.rs │ │ └── python_facet.rs │ └── rust │ │ ├── mod.rs │ │ └── rust_facet.rs │ ├── framework_detector.rs │ ├── lang │ ├── go.rs │ ├── js.rs │ ├── jvm.rs │ ├── mod.rs │ └── rust.rs │ └── lib.rs ├── justfile ├── locales ├── templates │ ├── concept │ │ ├── deploy-patterns.md │ │ └── sla.md │ ├── local.hbs │ ├── modeling │ │ └── practise.md │ └── pipeline │ │ └── practise.md └── translate │ ├── en-US │ └── suggest.ftl │ └── zh-CN │ └── suggest.ftl ├── plugins ├── coco_container │ ├── Cargo.toml │ ├── README.md │ └── src │ │ ├── coco_container_plugin.rs │ │ └── lib.rs ├── coco_pipeline │ ├── Cargo.toml │ ├── README.md │ └── src │ │ ├── coco_pipeline.rs │ │ ├── coco_pipeline_plugin.rs │ │ ├── github_action.rs │ │ ├── jenkinsfile.rs │ │ └── lib.rs ├── coco_struct │ ├── Cargo.toml │ ├── LICENSE │ ├── README.md │ └── src │ │ ├── coco_struct_plugin.rs │ │ ├── ctags │ │ ├── ctags_cmd.rs │ │ ├── ctags_opt.rs │ │ ├── ctags_parser.rs │ │ └── mod.rs │ │ ├── lib.rs │ │ └── plantuml │ │ ├── mod.rs │ │ └── plantuml_render.rs └── coco_swagger │ ├── Cargo.toml │ ├── README.md │ └── src │ ├── coco_swagger_plugin.rs │ └── lib.rs ├── psa ├── Cargo.toml ├── README.md └── src │ ├── dependency_analyzer.rs │ ├── files.rs │ ├── jvm │ ├── maven_dependency.rs │ ├── maven_module.rs │ ├── mod.rs │ └── psa_maven.rs │ ├── lib.rs │ ├── module_analyzer.rs │ ├── pas_content_root.rs │ ├── project_structure_analyzer.rs │ ├── psa_dependency.rs │ ├── psa_facet.rs │ ├── psa_module.rs │ └── psa_project.rs ├── src ├── app │ ├── analysis │ │ ├── architecture_analysis.rs │ │ ├── cloc_analysis.rs │ │ ├── framework_analysis.rs │ │ ├── git_analysis │ │ │ ├── branch_analysis.rs │ │ │ ├── commit_analysis.rs │ │ │ ├── file_analysis.rs │ │ │ ├── format_branch.rs │ │ │ ├── mod.rs │ │ │ └── tag_analysis.rs │ │ └── mod.rs │ ├── mod.rs │ ├── plugin_helper.rs │ ├── plugin_manager.rs │ ├── suggest │ │ ├── mod.rs │ │ └── suggester.rs │ └── visual │ │ ├── local_server.rs │ │ ├── mod.rs │ │ └── output_static.rs ├── bin │ ├── coco.rs │ ├── suggest.rs │ └── visual.rs ├── coco_error.rs ├── domain │ ├── architecture │ │ └── mod.rs │ ├── cli_opt │ │ ├── coco_opt.rs │ │ ├── mod.rs │ │ ├── suggest_opt.rs │ │ └── visual_opt.rs │ ├── cloc │ │ ├── cloc_language.rs │ │ └── mod.rs │ ├── code │ │ ├── code_relation.rs │ │ ├── file_include.rs │ │ └── mod.rs │ ├── framework │ │ ├── coco_framework.rs │ │ └── mod.rs │ ├── git │ │ ├── coco_branch.rs │ │ ├── coco_commit.rs │ │ ├── coco_commit_message.rs │ │ ├── coco_tag.rs │ │ └── mod.rs │ ├── mod.rs │ └── suggest │ │ ├── git_suggest.rs │ │ ├── mod.rs │ │ ├── model_suggest.rs │ │ ├── physical_suggest.rs │ │ └── pipeline_suggest.rs ├── infrastructure │ ├── cloc │ │ └── mod.rs │ ├── file_scanner │ │ └── mod.rs │ ├── git │ │ ├── cmd_git.rs │ │ ├── git_branch.rs │ │ ├── git_commit_message.rs │ │ ├── git_file_history.rs │ │ ├── git_log_parser.rs │ │ ├── git_repository.rs │ │ ├── git_tag_parser.rs │ │ └── mod.rs │ ├── mod.rs │ ├── time_format.rs │ └── translator │ │ ├── mod.rs │ │ └── translator.rs └── lib.rs └── web ├── explorer.html ├── fake ├── .gitignore └── .gitkeeep ├── index.html └── public ├── css ├── context-menu.css ├── layout.css ├── reset.css ├── slide.css └── tooltip.css ├── images └── favicon.ico └── js ├── graph-config.js ├── graph ├── cloc │ ├── code-flower.js │ ├── module-circle-packing.js │ └── module-nested-treemap.js ├── git │ ├── branch-timeline.js │ ├── commit-calendar.js │ ├── commit-code-frequency.js │ ├── commit-hour-heatmap.js │ ├── commits-tree.js │ ├── file-explorer.js │ ├── tags-timeline.js │ ├── team-learning-curve.js │ ├── team-lifecycle.js │ └── time-interactive-line.js └── plugins │ ├── pipeline-visual.js │ └── struct-visual.js ├── index.js ├── libs └── d3.min.js ├── plugins ├── code-layout.js ├── d3-voronoi-map.js ├── d3-voronoi-treemap.js ├── d3-weighted-voronoi.js ├── legend.js └── seedrandom.min.js └── support ├── code-support.js ├── commit-convert.js ├── dom-support.js ├── menu-support.js └── time-support.js /.adr.json: -------------------------------------------------------------------------------- 1 | {"language":"en","path":"docs/adr/","prefix":"","digits":4} -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | indent_style = space 6 | indent_size = 2 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | 10 | [*.md] 11 | trim_trailing_whitespace = false 12 | 13 | [*.rs] 14 | end_of_line = lf 15 | charset = utf-8 16 | trim_trailing_whitespace = true 17 | indent_style = space 18 | indent_size = 4 19 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | _fixtures/* linguist-vendored=true 2 | web/public/js/libs/* linguist-vendored=true 3 | web/public/js/plugins/* linguist-vendored=true 4 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 🐞 Bug report 3 | about: Create a report about something that is not working 4 | --- 5 | 6 | 7 | ### Describe the bug 8 | A clear and concise description of what the bug is. 9 | 10 | ### Steps to reproduce (please include code) 11 | 12 | 13 | ### Environment 14 | - coco version 15 | - Rust version 16 | - OS: [e.g. OSX 10.13.4, Windows 10] 17 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "\U0001F680 Feature Request" 3 | about: "I have a suggestion (and may want to implement it \U0001F642)!" 4 | title: '' 5 | labels: 'i: enhancement, i: needs triage' 6 | assignees: '' 7 | 8 | --- 9 | 10 | ## Feature Request 11 | 12 | **Is your feature request related to a problem? Please describe.** 13 | A clear and concise description of what the problem is. Ex. I have an issue when [...] 14 | 15 | **Describe the solution you'd like** 16 | A clear and concise description of what you want to happen. Add any considered drawbacks. 17 | 18 | **Describe alternatives you've considered** 19 | A clear and concise description of any alternative solutions or features you've considered. 20 | 21 | **Teachability, Documentation, Adoption, Migration Strategy** 22 | If you can, explain how users will be able to use this and possibly write out a version the docs. 23 | Maybe a screenshot or design? 24 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Coco Build 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | strategy: 8 | matrix: 9 | os: [macos-latest, ubuntu-latest, windows-latest] 10 | runs-on: ${{ matrix.os }} 11 | steps: 12 | - uses: actions/checkout@v2 13 | - name: Checkout submodules 14 | shell: bash 15 | run: | 16 | git fetch --tags 17 | auth_header="$(git config --local --get http.https://github.com/.extraheader)" 18 | git submodule sync --recursive 19 | git -c "http.extraheader=$auth_header" -c protocol.version=2 submodule update --init --force --recursive --depth=1 20 | 21 | - name: Install Just 22 | run: cargo install just 23 | 24 | - name: Build 25 | run: ${{matrix.ENV_VARS}} just release 26 | 27 | - name: Build Debug for Testing 28 | run: ${{matrix.ENV_VARS}} cargo build --all 29 | 30 | - name: Install ctags on Linux 31 | if: matrix.os == 'ubuntu-latest' 32 | run: | 33 | sudo snap install universal-ctags 34 | 35 | - name: Install ctags on macOS 36 | if: matrix.os == 'macOS-latest' 37 | run: | 38 | brew update 39 | brew install --HEAD universal-ctags/universal-ctags/universal-ctags 40 | 41 | - name: Install ctags on Windows 42 | if: matrix.os == 'windows-latest' 43 | run: | 44 | choco install universal-ctags 45 | 46 | - name: Debug Ctags 47 | run: ctags --version 48 | 49 | - name: Run unit tests 50 | run: ${{matrix.ENV_VARS}} just tests 51 | 52 | - name: Run e2e 53 | run: ${{matrix.ENV_VARS}} just e2e 54 | -------------------------------------------------------------------------------- /.github/workflows/coverage.yml: -------------------------------------------------------------------------------- 1 | # from: https://github.com/clap-rs/clap/blob/master/.github/workflows/coverage.yml 2 | name: Coverage 3 | on: 4 | pull_request: 5 | branches: [master] 6 | push: 7 | branches: [master] 8 | concurrency: 9 | group: coverage-${{ github.ref }} 10 | cancel-in-progress: true 11 | jobs: 12 | coverage: 13 | name: Coverage 14 | continue-on-error: true 15 | runs-on: ubuntu-latest 16 | steps: 17 | - name: Install rust 18 | uses: actions-rs/toolchain@v1 19 | with: 20 | toolchain: nightly 21 | components: llvm-tools-preview 22 | override: true 23 | 24 | - name: Checkout 25 | uses: actions/checkout@v2 26 | 27 | - name: Install llvm-cov 28 | uses: actions-rs/install@v0.1 29 | with: 30 | crate: cargo-llvm-cov 31 | version: 0.1.0-alpha.4 32 | use-tool-cache: true 33 | 34 | - name: Build Plugins 35 | run: ${{matrix.ENV_VARS}} cargo build --all 36 | 37 | - name: Install ctags on Linux 38 | if: matrix.os == 'ubuntu-latest' 39 | run: | 40 | sudo snap install universal-ctags 41 | 42 | - name: Coverage 43 | uses: actions-rs/cargo@v1 44 | with: 45 | command: llvm-cov 46 | args: --all-features --workspace --lcov --output-path lcov.info 47 | 48 | - name: Coveralls 49 | uses: coverallsapp/github-action@master 50 | with: 51 | path-to-lcov: lcov.info 52 | github-token: ${{ secrets.github_token }} 53 | 54 | - name: Upload coverage to Codecov 55 | uses: codecov/codecov-action@v1 56 | with: 57 | token: ${{ secrets.CODECOV_TOKEN }} # not required for public repos 58 | files: lcov.info 59 | fail_ci_if_error: true 60 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | debug/ 4 | target/ 5 | 6 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 7 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 8 | Cargo.lock 9 | 10 | # These are backup files generated by rustfmt 11 | **/*.rs.bk 12 | .idea 13 | .coco 14 | web/hack.js 15 | 16 | coco_static 17 | web/public/js/libs/d3.js 18 | coco_plugins 19 | temp 20 | .DS_Store -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .idea 2 | docs/ 3 | .adr.json 4 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "coco" 3 | version = "0.6.0" 4 | authors = ["Inherd Group ", "Phodal Huang "] 5 | edition = "2018" 6 | license = "MIT" 7 | readme = "README.md" 8 | repository = "https://github.com/inherd/coco" 9 | documentation = "https://github.com/inherd/coco" 10 | homepage = "https://github.com/inherd/coco" 11 | description = """ 12 | Coco 是一个研发效能分析工具,如开发速率(根据架构复杂度及行数变更)、团队演进、历史分析、根因分析等。生成可视化报告,并对应的改进建议。 A DevOps efficiency analysis and auto-suggestion tool. 13 | """ 14 | categories = ["text-processing", "command-line-interface", "development-tools"] 15 | exclude = [ 16 | ".coco/*", 17 | "benchmark/*", 18 | "fixtures/*", 19 | "targets/*", 20 | ".github/*", 21 | ".gitattributes", 22 | ".adr.json", 23 | ] 24 | 25 | [dependencies] 26 | failure = "0.1.8" 27 | 28 | lazy_static = "1.4.0" 29 | 30 | # time format 31 | chrono = "0.4" 32 | 33 | git2 = "0.13" 34 | git-scanner = "0.2.0" 35 | # for cli usage 36 | clap = "2.33.3" 37 | 38 | # parse for url 39 | url = "2.2.0" 40 | 41 | # cloc 42 | tokei = "12.1.2" 43 | 44 | # serializing and deserializing 45 | serde = { version = "1.0", features = ["derive"] } 46 | serde_json = "1.0" 47 | serde_yaml = "0.8" # for config parse 48 | 49 | # Parallel 50 | rayon = "1.5" 51 | 52 | walkdir = "2" 53 | 54 | regex = "1" 55 | 56 | # grammar generator 57 | pest = "2.1.3" 58 | pest_derive = "2.1.0" 59 | 60 | # embed file 61 | rust-embed = "5.9.0" 62 | 63 | # visual for web 64 | actix-web = { version = "3", default-features = false } 65 | actix-rt = "2.0.0" 66 | mime_guess = { version = "2" } 67 | 68 | # cli prompt 69 | #dialoguer = "0.7.1" 70 | dialoguer = "0.10.2" 71 | 72 | # open URLs in browsers 73 | webbrowser = "0.5.5" 74 | 75 | # download plugins 76 | reqwest = { version = "0.11", features = ["blocking", "json"] } 77 | zip = "0.5" 78 | 79 | # git history content type 80 | content_inspector = "0.2.4" 81 | 82 | # suggest translation 83 | fluent = "0.15.0" 84 | unic-langid = { version = "0.9.0", features = ["macros"] } 85 | 86 | # cli options to struct 87 | structopt = "0.3" 88 | 89 | # plugin manager 90 | dlopen = "0.1.8" 91 | dlopen_derive = "0.1.4" 92 | [dependencies.framework] 93 | path = "framework" 94 | 95 | [dependencies.core_model] 96 | path = "core_model" 97 | 98 | [workspace] 99 | members = [ 100 | 'framework', 101 | 'e2e', 102 | 'core_model', 103 | 'plugins/coco_swagger', 104 | 'plugins/coco_struct', 105 | 'plugins/coco_pipeline', 106 | 'plugins/coco_container', 107 | 'psa', 108 | ] 109 | 110 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Inherd Group 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-slate -------------------------------------------------------------------------------- /_fixtures/config.yml: -------------------------------------------------------------------------------- 1 | repos: 2 | - url: https://github.com/coco-rs/coco.fixtures 3 | - url: https://github.com/coco-rs/coco.fixtures2 4 | 5 | message: 6 | # should be a regexp 7 | format: [] 8 | -------------------------------------------------------------------------------- /_fixtures/ctags/coco_class_tags: -------------------------------------------------------------------------------- 1 | !_TAG_FILE_FORMAT 2 /extended format; --format=1 will not append ;" to lines/ 2 | !_TAG_FILE_SORTED 1 /0=unsorted, 1=sorted, 2=foldcase/ 3 | !_TAG_OUTPUT_EXCMD mixed /number, pattern, mixed, or combineV2/ 4 | !_TAG_OUTPUT_FILESEP slash /slash or backslash/ 5 | !_TAG_OUTPUT_MODE u-ctags /u-ctags or e-ctags/ 6 | !_TAG_PATTERN_LENGTH_LIMIT 96 /0 for no limit/ 7 | !_TAG_PROC_CWD coco/ // 8 | !_TAG_PROGRAM_AUTHOR Universal Ctags Team // 9 | !_TAG_PROGRAM_NAME Universal Ctags /Derived from Exuberant Ctags/ 10 | !_TAG_PROGRAM_URL https://ctags.io/ /official site/ 11 | !_TAG_PROGRAM_VERSION 5.9.0 /d532b5c/ 12 | ClassInfo src/coco_struct.rs /^impl ClassInfo {$/;" implementation line:48 language:Rust 13 | ClassInfo src/coco_struct.rs /^pub struct ClassInfo {$/;" struct line:38 language:Rust 14 | MemberInfo src/coco_struct.rs /^impl MemberInfo {$/;" implementation line:10 language:Rust 15 | MemberInfo src/coco_struct.rs /^pub struct MemberInfo {$/;" struct line:4 language:Rust 16 | MethodInfo src/coco_struct.rs /^impl MethodInfo {$/;" implementation line:27 language:Rust 17 | MethodInfo src/coco_struct.rs /^pub struct MethodInfo {$/;" struct line:21 language:Rust 18 | access src/coco_struct.rs /^ pub access: String,$/;" field line:23 language:Rust struct:MethodInfo 19 | access src/coco_struct.rs /^ pub access: String,$/;" field line:6 language:Rust struct:MemberInfo 20 | data_type src/coco_struct.rs /^ pub data_type: String,$/;" field line:7 language:Rust struct:MemberInfo 21 | file src/coco_struct.rs /^ pub file: String,$/;" field line:41 language:Rust struct:ClassInfo 22 | id src/coco_struct.rs /^ pub id: i32,$/;" field line:40 language:Rust struct:ClassInfo 23 | lang src/coco_struct.rs /^ pub lang: String,$/;" field line:42 language:Rust struct:ClassInfo 24 | members src/coco_struct.rs /^ pub members: Vec,$/;" field line:44 language:Rust struct:ClassInfo 25 | methods src/coco_struct.rs /^ pub methods: Vec,$/;" field line:45 language:Rust struct:ClassInfo 26 | name src/coco_struct.rs /^ pub name: String,$/;" field line:22 language:Rust struct:MethodInfo 27 | name src/coco_struct.rs /^ pub name: String,$/;" field line:39 language:Rust struct:ClassInfo 28 | name src/coco_struct.rs /^ pub name: String,$/;" field line:5 language:Rust struct:MemberInfo 29 | new src/coco_struct.rs /^ pub fn new(class_name: &str) -> Self {$/;" method line:49 language:Rust implementation:ClassInfo 30 | new src/coco_struct.rs /^ pub fn new(name: &str, access: &str, data_type: String) -> Self {$/;" method line:11 language:Rust implementation:MemberInfo 31 | new src/coco_struct.rs /^ pub fn new(name: &str, access: &str, data_type: String) -> Self {$/;" method line:28 language:Rust implementation:MethodInfo 32 | parents src/coco_struct.rs /^ pub parents: Vec,$/;" field line:43 language:Rust struct:ClassInfo 33 | return_type src/coco_struct.rs /^ pub return_type: String,$/;" field line:24 language:Rust struct:MethodInfo 34 | -------------------------------------------------------------------------------- /_fixtures/ctags/coco_tags: -------------------------------------------------------------------------------- 1 | !_TAG_FILE_FORMAT 2 /extended format; --format=1 will not append ;" to lines/ 2 | !_TAG_FILE_SORTED 1 /0=unsorted, 1=sorted, 2=foldcase/ 3 | !_TAG_OUTPUT_EXCMD mixed /number, pattern, mixed, or combineV2/ 4 | !_TAG_OUTPUT_FILESEP slash /slash or backslash/ 5 | !_TAG_OUTPUT_MODE u-ctags /u-ctags or e-ctags/ 6 | !_TAG_PATTERN_LENGTH_LIMIT 96 /0 for no limit/ 7 | !_TAG_PROC_CWD /Users/fdhuang/consultant/devops/coco/ // 8 | !_TAG_PROGRAM_AUTHOR Universal Ctags Team // 9 | !_TAG_PROGRAM_NAME Universal Ctags /Derived from Exuberant Ctags/ 10 | !_TAG_PROGRAM_URL https://ctags.io/ /official site/ 11 | !_TAG_PROGRAM_VERSION 5.9.0 /d532b5c/ 12 | CocoSwagger coco_swagger/src/lib.rs /^impl Default for CocoSwagger {$/;" implementation line:20 language:Rust 13 | CocoSwagger coco_swagger/src/lib.rs /^impl PluginInterface for CocoSwagger {$/;" implementation line:6 language:Rust 14 | CocoSwagger coco_swagger/src/lib.rs /^pub struct CocoSwagger {}$/;" struct line:4 language:Rust 15 | default coco_swagger/src/lib.rs /^ fn default() -> Self {$/;" method line:21 language:Rust implementation:CocoSwagger 16 | execute coco_swagger/src/lib.rs /^ fn execute(&self, config: CocoConfig) {$/;" method line:15 language:Rust implementation:CocoSwagger 17 | name coco_swagger/src/lib.rs /^ fn name(&self) -> &'static str {$/;" method line:7 language:Rust implementation:CocoSwagger 18 | on_plugin_load coco_swagger/src/lib.rs /^ fn on_plugin_load(&self) {}$/;" method line:11 language:Rust implementation:CocoSwagger 19 | on_plugin_unload coco_swagger/src/lib.rs /^ fn on_plugin_unload(&self) {}$/;" method line:13 language:Rust implementation:CocoSwagger 20 | plugin coco_swagger/src/lib.rs /^pub fn plugin() -> Box {$/;" function line:27 language:Rust 21 | -------------------------------------------------------------------------------- /_fixtures/ctags/go_tags: -------------------------------------------------------------------------------- 1 | !_TAG_FILE_FORMAT 2 /extended format; --format=1 will not append ;" to lines/ 2 | !_TAG_FILE_SORTED 1 /0=unsorted, 1=sorted, 2=foldcase/ 3 | !_TAG_OUTPUT_EXCMD mixed /number, pattern, mixed, or combineV2/ 4 | !_TAG_OUTPUT_FILESEP slash /slash or backslash/ 5 | !_TAG_OUTPUT_MODE u-ctags /u-ctags or e-ctags/ 6 | !_TAG_PATTERN_LENGTH_LIMIT 96 /0 for no limit/ 7 | !_TAG_PROC_CWD /Users/fdhuang/consultant/devops/coco/ // 8 | !_TAG_PROGRAM_AUTHOR Universal Ctags Team // 9 | !_TAG_PROGRAM_NAME Universal Ctags /Derived from Exuberant Ctags/ 10 | !_TAG_PROGRAM_URL https://ctags.io/ /official site/ 11 | !_TAG_PROGRAM_VERSION 5.9.0 /d532b5c/ 12 | InitDatastore pkg/datastore.go /^func InitDatastore() {$/;" func line:37 language:Go package:pkg 13 | access pkg/datastore.go /^ name, access, datatype string$/;" member line:19 language:Go struct:pkg.memberinfo_st typeref:typename:string 14 | access pkg/datastore.go /^ name, access, returntype string$/;" member line:23 language:Go struct:pkg.methodinfo_st typeref:typename:string 15 | classinfo_st pkg/datastore.go /^type classinfo_st struct {$/;" struct line:26 language:Go package:pkg 16 | classmap pkg/datastore.go /^var classmap map[string]classinfo_st$/;" var line:34 language:Go package:pkg typeref:typename:map[string]classinfo_st 17 | datatype pkg/datastore.go /^ name, access, datatype string$/;" member line:19 language:Go struct:pkg.memberinfo_st typeref:typename:string 18 | id pkg/datastore.go /^ id int$/;" member line:28 language:Go struct:pkg.classinfo_st typeref:typename:int 19 | idcounter pkg/datastore.go /^var idcounter int = 1$/;" var line:35 language:Go package:pkg typeref:typename:int 20 | memberinfo_st pkg/datastore.go /^type memberinfo_st struct {$/;" struct line:18 language:Go package:pkg 21 | members pkg/datastore.go /^ members []memberinfo_st$/;" member line:30 language:Go struct:pkg.classinfo_st typeref:typename:[]memberinfo_st 22 | methodinfo_st pkg/datastore.go /^type methodinfo_st struct {$/;" struct line:22 language:Go package:pkg 23 | methods pkg/datastore.go /^ methods []methodinfo_st$/;" member line:31 language:Go struct:pkg.classinfo_st typeref:typename:[]methodinfo_st 24 | name pkg/datastore.go /^ name string$/;" member line:27 language:Go struct:pkg.classinfo_st typeref:typename:string 25 | name pkg/datastore.go /^ name, access, datatype string$/;" member line:19 language:Go struct:pkg.memberinfo_st typeref:typename:string 26 | name pkg/datastore.go /^ name, access, returntype string$/;" member line:23 language:Go struct:pkg.methodinfo_st typeref:typename:string 27 | parents pkg/datastore.go /^ parents []string$/;" member line:29 language:Go struct:pkg.classinfo_st typeref:typename:[]string 28 | pkg pkg/datastore.go /^package pkg$/;" package line:16 language:Go 29 | returntype pkg/datastore.go /^ name, access, returntype string$/;" member line:23 language:Go struct:pkg.methodinfo_st typeref:typename:string 30 | -------------------------------------------------------------------------------- /_fixtures/ctags/java_tags: -------------------------------------------------------------------------------- 1 | !_TAG_FILE_FORMAT 2 /extended format; --format=1 will not append ;" to lines/ 2 | !_TAG_FILE_SORTED 1 /0=unsorted, 1=sorted, 2=foldcase/ 3 | !_TAG_OUTPUT_EXCMD mixed /number, pattern, mixed, or combineV2/ 4 | !_TAG_OUTPUT_FILESEP slash /slash or backslash/ 5 | !_TAG_OUTPUT_MODE u-ctags /u-ctags or e-ctags/ 6 | !_TAG_PATTERN_LENGTH_LIMIT 96 /0 for no limit/ 7 | !_TAG_PROC_CWD /Users/fdhuang/clone/lombok/src/core/lombok/core/configuration/ // 8 | !_TAG_PROGRAM_AUTHOR Universal Ctags Team // 9 | !_TAG_PROGRAM_NAME Universal Ctags /Derived from Exuberant Ctags/ 10 | !_TAG_PROGRAM_URL https://ctags.io/ /official site/ 11 | !_TAG_PROGRAM_VERSION 5.9.0 /d532b5c/ 12 | TypeName TypeName.java /^ private TypeName(String name) {$/;" method line:29 language:Java class:TypeName file: access:private 13 | TypeName TypeName.java /^public final class TypeName implements ConfigurationValueType {$/;" class line:26 language:Java inherits:ConfigurationValueType 14 | description TypeName.java /^ public static String description() {$/;" method line:43 language:Java class:TypeName access:public 15 | equals TypeName.java /^ @Override public boolean equals(Object obj) {$/;" method line:51 language:Java class:TypeName access:public 16 | exampleValue TypeName.java /^ public static String exampleValue() {$/;" method line:47 language:Java class:TypeName access:public 17 | getCharArray TypeName.java /^ public char[] getCharArray() {$/;" method line:68 language:Java class:TypeName access:public 18 | getName TypeName.java /^ public String getName() {$/;" method line:64 language:Java class:TypeName access:public 19 | hashCode TypeName.java /^ @Override public int hashCode() {$/;" method line:56 language:Java class:TypeName access:public 20 | lombok.core.configuration TypeName.java /^package lombok.core.configuration;$/;" package line:22 language:Java 21 | name TypeName.java /^ private final String name;$/;" field line:27 language:Java class:TypeName file: access:private 22 | toString TypeName.java /^ @Override public String toString() {$/;" method line:60 language:Java class:TypeName access:public 23 | valueOf TypeName.java /^ public static TypeName valueOf(String name) {$/;" method line:33 language:Java class:TypeName access:public 24 | -------------------------------------------------------------------------------- /_fixtures/ctags/main.go: -------------------------------------------------------------------------------- 1 | func main() { 2 | 3 | } 4 | 5 | type person struct { 6 | name string 7 | age int 8 | } 9 | -------------------------------------------------------------------------------- /_fixtures/ctags/source/TypeName.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013-2019 The Project Lombok Authors. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | package lombok.core.configuration; 23 | 24 | import lombok.core.JavaIdentifiers; 25 | 26 | public final class TypeName implements ConfigurationValueType { 27 | private final String name; 28 | 29 | private TypeName(String name) { 30 | this.name = name; 31 | } 32 | 33 | public static TypeName valueOf(String name) { 34 | if (name == null || name.trim().isEmpty()) return null; 35 | 36 | String trimmedName = name.trim(); 37 | for (String identifier : trimmedName.split("\\.")) { 38 | if (!JavaIdentifiers.isValidJavaIdentifier(identifier)) throw new IllegalArgumentException("Invalid type name " + trimmedName + " (part " + identifier + ")"); 39 | } 40 | return new TypeName(trimmedName); 41 | } 42 | 43 | public static String description() { 44 | return "type-name"; 45 | } 46 | 47 | public static String exampleValue() { 48 | return ""; 49 | } 50 | 51 | @Override public boolean equals(Object obj) { 52 | if (!(obj instanceof TypeName)) return false; 53 | return name.equals(((TypeName) obj).name); 54 | } 55 | 56 | @Override public int hashCode() { 57 | return name.hashCode(); 58 | } 59 | 60 | @Override public String toString() { 61 | return name; 62 | } 63 | 64 | public String getName() { 65 | return name; 66 | } 67 | 68 | public char[] getCharArray() { 69 | return name.toCharArray(); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /_fixtures/ctags/source/animal.ts: -------------------------------------------------------------------------------- 1 | // https://github.com/microsoft/TypeScriptSamples/blob/master/simple/animals.ts 2 | 3 | class Animal { 4 | name : string 5 | constructor(name : string) { 6 | this.name = name; 7 | } 8 | move(meters) { 9 | console.log(this.name + " moved " + meters + "m."); 10 | } 11 | } 12 | 13 | class Snake extends Animal { 14 | move() { 15 | console.log("Slithering..."); 16 | super.move(5); 17 | } 18 | } 19 | 20 | class Horse extends Animal { 21 | move() { 22 | console.log("Galloping..."); 23 | super.move(45); 24 | } 25 | } 26 | 27 | var sam = new Snake("Sammy the Python") 28 | var tom: Animal = new Horse("Tommy the Palomino") 29 | 30 | sam.move() 31 | tom.move(34) 32 | -------------------------------------------------------------------------------- /_fixtures/ctags/source/datastore.go: -------------------------------------------------------------------------------- 1 | // tags2uml 2 | // Copyright 2014 ruben2020 https://github.com/ruben2020/ 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | package pkg 17 | 18 | type memberinfo_st struct { 19 | name, access, datatype string 20 | } 21 | 22 | type methodinfo_st struct { 23 | name, access, returntype string 24 | } 25 | 26 | type classinfo_st struct { 27 | name string 28 | id int 29 | parents []string 30 | members []memberinfo_st 31 | methods []methodinfo_st 32 | } 33 | 34 | var classmap map[string]classinfo_st 35 | var idcounter int = 1 36 | 37 | func InitDatastore() { 38 | classmap = make(map[string]classinfo_st) 39 | } 40 | -------------------------------------------------------------------------------- /_fixtures/ctags/ts_tags: -------------------------------------------------------------------------------- 1 | !_TAG_FILE_FORMAT 2 /extended format; --format=1 will not append ;" to lines/ 2 | !_TAG_FILE_SORTED 1 /0=unsorted, 1=sorted, 2=foldcase/ 3 | !_TAG_OUTPUT_EXCMD mixed /number, pattern, mixed, or combineV2/ 4 | !_TAG_OUTPUT_FILESEP slash /slash or backslash/ 5 | !_TAG_OUTPUT_MODE u-ctags /u-ctags or e-ctags/ 6 | !_TAG_PATTERN_LENGTH_LIMIT 96 /0 for no limit/ 7 | !_TAG_PROC_CWD /Users/chalme/CLionProjects/coco/_fixtures/ctags/source/ // 8 | !_TAG_PROGRAM_AUTHOR Universal Ctags Team // 9 | !_TAG_PROGRAM_NAME Universal Ctags /Derived from Exuberant Ctags/ 10 | !_TAG_PROGRAM_URL https://ctags.io/ /official site/ 11 | !_TAG_PROGRAM_VERSION 5.9.0 /98ff77d/ 12 | Animal animal.ts /^class Animal {$/;" class line:3 language:TypeScript 13 | Horse animal.ts /^class Horse extends Animal {$/;" class line:20 language:TypeScript inherits:Animal 14 | Snake animal.ts /^class Snake extends Animal {$/;" class line:13 language:TypeScript inherits:Animal 15 | constructor animal.ts /^ constructor(name : string) {$/;" method line:5 language:TypeScript class:Animal access:public 16 | move animal.ts /^ move() {$/;" method line:14 language:TypeScript class:Snake access:public 17 | move animal.ts /^ move() {$/;" method line:21 language:TypeScript class:Horse access:public 18 | move animal.ts /^ move(meters) {$/;" method line:8 language:TypeScript class:Animal access:public 19 | name animal.ts /^ name : string$/;" property line:4 language:TypeScript class:Animal access:public 20 | sam animal.ts /^var sam = new Snake("Sammy the Python")$/;" variable line:27 language:TypeScript 21 | tom animal.ts /^var tom: Animal = new Horse("Tommy the Palomino")$/;" variable line:28 language:TypeScript 22 | -------------------------------------------------------------------------------- /_fixtures/dockerfile/Go.Dockerfile: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # [START cloudrun_pubsub_dockerfile] 16 | # [START run_pubsub_dockerfile] 17 | 18 | # Use the offical golang image to create a binary. 19 | # This is based on Debian and sets the GOPATH to /go. 20 | # https://hub.docker.com/_/golang 21 | FROM golang:1.16-buster as builder 22 | 23 | # Create and change to the app directory. 24 | WORKDIR /app 25 | 26 | # Retrieve application dependencies. 27 | # This allows the container build to reuse cached dependencies. 28 | # Expecting to copy go.mod and if present go.sum. 29 | COPY go.* ./ 30 | RUN go mod download 31 | 32 | # Copy local code to the container image. 33 | COPY . ./ 34 | 35 | # Build the binary. 36 | RUN go build -mod=readonly -v -o server 37 | 38 | # Use the official Debian slim image for a lean production container. 39 | # https://hub.docker.com/_/debian 40 | # https://docs.docker.com/develop/develop-images/multistage-build/#use-multi-stage-builds 41 | FROM debian:buster-slim 42 | RUN set -x && apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y \ 43 | ca-certificates && \ 44 | rm -rf /var/lib/apt/lists/* 45 | 46 | # Copy the binary to the production image from the builder stage. 47 | COPY --from=builder /app/server /server 48 | 49 | # Run the web service on container startup. 50 | CMD ["/server"] 51 | 52 | # [END run_pubsub_dockerfile] 53 | # [END cloudrun_pubsub_dockerfile] 54 | -------------------------------------------------------------------------------- /_fixtures/pipeline/jenkinsfile/Jenkinsfile: -------------------------------------------------------------------------------- 1 | 2 | pipeline { 3 | agent { 4 | label "docker-compose" 5 | } 6 | options { 7 | buildDiscarder(logRotator(numToKeepStr: "2")) 8 | disableConcurrentBuilds() 9 | } 10 | stages { 11 | stage("versions") { 12 | steps { 13 | sh "java -version" 14 | sh "docker version" 15 | sh "docker-compose version" 16 | } 17 | } 18 | stage("test-go") { 19 | agent { 20 | label "go" 21 | } 22 | steps { 23 | sh "go get -d -v -t && go test --cover -v ./... --run UnitTest && go build -v -o go-demo" 24 | } 25 | } 26 | stage("test-docker") { 27 | steps { 28 | sh "docker container run -v ${workspace}:/usr/src/myapp -w /usr/src/myapp golang:1.9 bash -c \"go get -d -v -t && go test --cover -v ./... --run UnitTest && go build -v -o go-demo\"" 29 | } 30 | } 31 | stage("test-dc") { 32 | steps { 33 | sh "docker-compose run --rm unit" 34 | } 35 | } 36 | stage("release") { 37 | when { 38 | branch "master" 39 | } 40 | steps { 41 | script { 42 | def dateFormat = new SimpleDateFormat("yy.MM.dd") 43 | currentBuild.displayName = dateFormat.format(new Date()) + "-" + env.BUILD_NUMBER 44 | } 45 | sh "docker image build -t vfarcic/go-demo-cje ." 46 | sh "docker image tag vfarcic/go-demo-cje vfarcic/go-demo-cje:${currentBuild.displayName}" 47 | withCredentials([usernamePassword( 48 | credentialsId: "docker", 49 | usernameVariable: "USER", 50 | passwordVariable: "PASS" 51 | )]) { 52 | sh "docker login -u '$USER' -p '$PASS'" 53 | } 54 | sh "docker image push vfarcic/go-demo-cje" 55 | sh "docker image push vfarcic/go-demo-cje:${currentBuild.displayName}" 56 | } 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /_fixtures/projects/go/hello/main.go: -------------------------------------------------------------------------------- 1 | func main() { 2 | } -------------------------------------------------------------------------------- /_fixtures/projects/go/simple/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/inherd/coco 2 | 3 | go 1.15 -------------------------------------------------------------------------------- /_fixtures/projects/java/hello/HelloWorld.java: -------------------------------------------------------------------------------- 1 | package hello; 2 | 3 | public class HelloWorld { 4 | public static void main(String []args) { 5 | System.out.println("Hello World"); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /_fixtures/projects/java/hello/pom.xml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inherd/coco/43ae3f3afd70705e0827d959e73efd89abc4a299/_fixtures/projects/java/hello/pom.xml -------------------------------------------------------------------------------- /_fixtures/projects/java/multi_mod_maven_project/module1/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | coco 7 | multi_mod_maven_project 8 | 1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | module2 13 | 1.0-SNAPSHOT 14 | 15 | 16 | 1.0.1.RELEASE 17 | other properties 18 | 19 | 20 | 21 | 22 | org.springframework.boot 23 | spring-boot-starter-web 24 | 2.0.0.RELEASE 25 | test 26 | 27 | 28 | org.springframework.boot 29 | spring-boot-starter-logging 30 | ${spring-boot-starter.version} 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /_fixtures/projects/java/multi_mod_maven_project/module1/src/main/java/Hello.java: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inherd/coco/43ae3f3afd70705e0827d959e73efd89abc4a299/_fixtures/projects/java/multi_mod_maven_project/module1/src/main/java/Hello.java -------------------------------------------------------------------------------- /_fixtures/projects/java/multi_mod_maven_project/module1/src/main/resources/application.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inherd/coco/43ae3f3afd70705e0827d959e73efd89abc4a299/_fixtures/projects/java/multi_mod_maven_project/module1/src/main/resources/application.yml -------------------------------------------------------------------------------- /_fixtures/projects/java/multi_mod_maven_project/module1/src/test/java/HelloTest.java: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inherd/coco/43ae3f3afd70705e0827d959e73efd89abc4a299/_fixtures/projects/java/multi_mod_maven_project/module1/src/test/java/HelloTest.java -------------------------------------------------------------------------------- /_fixtures/projects/java/multi_mod_maven_project/module1/src/test/resources/application.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inherd/coco/43ae3f3afd70705e0827d959e73efd89abc4a299/_fixtures/projects/java/multi_mod_maven_project/module1/src/test/resources/application.yml -------------------------------------------------------------------------------- /_fixtures/projects/java/multi_mod_maven_project/module2/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | coco 7 | multi_mod_maven_project 8 | 1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | module1 13 | 1.0-SNAPSHOT 14 | 15 | 16 | 1.0.2.RELEASE 17 | other properties 18 | 19 | 20 | 21 | 22 | org.springframework.boot 23 | spring-boot-starter-web 24 | 2.0.0.RELEASE 25 | test 26 | 27 | 28 | org.springframework.boot 29 | spring-boot-starter-logging 30 | ${spring-boot-starter.version} 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /_fixtures/projects/java/multi_mod_maven_project/module2/src/main/java/Hello.java: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inherd/coco/43ae3f3afd70705e0827d959e73efd89abc4a299/_fixtures/projects/java/multi_mod_maven_project/module2/src/main/java/Hello.java -------------------------------------------------------------------------------- /_fixtures/projects/java/multi_mod_maven_project/module2/src/main/resources/application.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inherd/coco/43ae3f3afd70705e0827d959e73efd89abc4a299/_fixtures/projects/java/multi_mod_maven_project/module2/src/main/resources/application.yml -------------------------------------------------------------------------------- /_fixtures/projects/java/multi_mod_maven_project/module2/src/test/java/HelloTest.java: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inherd/coco/43ae3f3afd70705e0827d959e73efd89abc4a299/_fixtures/projects/java/multi_mod_maven_project/module2/src/test/java/HelloTest.java -------------------------------------------------------------------------------- /_fixtures/projects/java/multi_mod_maven_project/module2/src/test/resources/application.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inherd/coco/43ae3f3afd70705e0827d959e73efd89abc4a299/_fixtures/projects/java/multi_mod_maven_project/module2/src/test/resources/application.yml -------------------------------------------------------------------------------- /_fixtures/projects/java/multi_mod_maven_project/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | coco 7 | coco 8 | 1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | multi_mod_maven_project 13 | 1.0-SNAPSHOT 14 | 15 | 16 | 1.0.0.RELEASE 17 | other properties 18 | 19 | 20 | 21 | 22 | org.springframework.boot 23 | spring-boot-starter-web 24 | 2.0.0.RELEASE 25 | test 26 | 27 | 28 | org.springframework.boot 29 | spring-boot-starter-logging 30 | ${spring-boot-starter.version} 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /_fixtures/projects/java/multi_mod_maven_project/src/main/java/Hello.java: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inherd/coco/43ae3f3afd70705e0827d959e73efd89abc4a299/_fixtures/projects/java/multi_mod_maven_project/src/main/java/Hello.java -------------------------------------------------------------------------------- /_fixtures/projects/java/multi_mod_maven_project/src/main/resources/application.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inherd/coco/43ae3f3afd70705e0827d959e73efd89abc4a299/_fixtures/projects/java/multi_mod_maven_project/src/main/resources/application.yml -------------------------------------------------------------------------------- /_fixtures/projects/java/multi_mod_maven_project/src/test/java/HelloTest.java: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inherd/coco/43ae3f3afd70705e0827d959e73efd89abc4a299/_fixtures/projects/java/multi_mod_maven_project/src/test/java/HelloTest.java -------------------------------------------------------------------------------- /_fixtures/projects/java/multi_mod_maven_project/src/test/resources/application.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inherd/coco/43ae3f3afd70705e0827d959e73efd89abc4a299/_fixtures/projects/java/multi_mod_maven_project/src/test/resources/application.yml -------------------------------------------------------------------------------- /_fixtures/projects/java/simple/.gitignore: -------------------------------------------------------------------------------- 1 | ignore.js 2 | .idea 3 | -------------------------------------------------------------------------------- /_fixtures/projects/java/simple/app/HelloWorld.java: -------------------------------------------------------------------------------- 1 | package hello; 2 | 3 | public class HelloWorld { 4 | public static void main(String []args) { 5 | System.out.println("Hello World"); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /_fixtures/projects/java/simple/app/pom.xml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inherd/coco/43ae3f3afd70705e0827d959e73efd89abc4a299/_fixtures/projects/java/simple/app/pom.xml -------------------------------------------------------------------------------- /_fixtures/projects/java/simple/build.gradle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inherd/coco/43ae3f3afd70705e0827d959e73efd89abc4a299/_fixtures/projects/java/simple/build.gradle -------------------------------------------------------------------------------- /_fixtures/projects/java/simple/ignore.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inherd/coco/43ae3f3afd70705e0827d959e73efd89abc4a299/_fixtures/projects/java/simple/ignore.js -------------------------------------------------------------------------------- /_fixtures/projects/java/simple/settings.gradle: -------------------------------------------------------------------------------- 1 | include ":app" -------------------------------------------------------------------------------- /_fixtures/projects/js/bowerproject/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "angular-cli": "angular/angular-cli#^9.1.3" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /_fixtures/projects/js/bowerproject/bower_components/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inherd/coco/43ae3f3afd70705e0827d959e73efd89abc4a299/_fixtures/projects/js/bowerproject/bower_components/.gitkeep -------------------------------------------------------------------------------- /_fixtures/projects/js/npmproject/node_modules/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inherd/coco/43ae3f3afd70705e0827d959e73efd89abc4a299/_fixtures/projects/js/npmproject/node_modules/.gitkeep -------------------------------------------------------------------------------- /_fixtures/projects/js/npmproject/package.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /_fixtures/projects/jvm/Hello.groovy: -------------------------------------------------------------------------------- 1 | class Hello { 2 | } -------------------------------------------------------------------------------- /_fixtures/projects/jvm/Hello.kt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inherd/coco/43ae3f3afd70705e0827d959e73efd89abc4a299/_fixtures/projects/jvm/Hello.kt -------------------------------------------------------------------------------- /_fixtures/projects/jvm/Hello.scala: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inherd/coco/43ae3f3afd70705e0827d959e73efd89abc4a299/_fixtures/projects/jvm/Hello.scala -------------------------------------------------------------------------------- /_fixtures/projects/jvm/build.gradle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inherd/coco/43ae3f3afd70705e0827d959e73efd89abc4a299/_fixtures/projects/jvm/build.gradle -------------------------------------------------------------------------------- /_fixtures/projects/jvm/mavenproject/Hello.kt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inherd/coco/43ae3f3afd70705e0827d959e73efd89abc4a299/_fixtures/projects/jvm/mavenproject/Hello.kt -------------------------------------------------------------------------------- /_fixtures/projects/jvm/mavenproject/HelloWorldTest.java: -------------------------------------------------------------------------------- 1 | public class HelloTest { 2 | } -------------------------------------------------------------------------------- /_fixtures/projects/jvm/mavenproject/Helloworld.java: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inherd/coco/43ae3f3afd70705e0827d959e73efd89abc4a299/_fixtures/projects/jvm/mavenproject/Helloworld.java -------------------------------------------------------------------------------- /_fixtures/projects/jvm/mavenproject/pom.xml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inherd/coco/43ae3f3afd70705e0827d959e73efd89abc4a299/_fixtures/projects/jvm/mavenproject/pom.xml -------------------------------------------------------------------------------- /_fixtures/projects/jvm/settings.gradle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inherd/coco/43ae3f3afd70705e0827d959e73efd89abc4a299/_fixtures/projects/jvm/settings.gradle -------------------------------------------------------------------------------- /_fixtures/projects/rust/cargo/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "coco" 3 | version = "0.0.1" 4 | authors = ["coco"] 5 | edition = "2018" 6 | [dependencies] -------------------------------------------------------------------------------- /_fixtures/repos/root/app1/.gittest: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inherd/coco/43ae3f3afd70705e0827d959e73efd89abc4a299/_fixtures/repos/root/app1/.gittest -------------------------------------------------------------------------------- /_fixtures/repos/root/app2/.gittest: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inherd/coco/43ae3f3afd70705e0827d959e73efd89abc4a299/_fixtures/repos/root/app2/.gittest -------------------------------------------------------------------------------- /_fixtures/swagger/petstore.yaml: -------------------------------------------------------------------------------- 1 | openapi: "3.0.0" 2 | info: 3 | version: 1.0.0 4 | title: Swagger Petstore 5 | license: 6 | name: MIT 7 | servers: 8 | - url: http://petstore.swagger.io/v1 9 | paths: 10 | /pets: 11 | get: 12 | summary: List all pets 13 | operationId: listPets 14 | tags: 15 | - pets 16 | parameters: 17 | - name: limit 18 | in: query 19 | description: How many items to return at one time (max 100) 20 | required: false 21 | schema: 22 | type: integer 23 | format: int32 24 | responses: 25 | '200': 26 | description: An paged array of pets 27 | headers: 28 | x-next: 29 | description: A link to the next page of responses 30 | schema: 31 | type: string 32 | content: 33 | application/json: 34 | schema: 35 | $ref: "#/components/schemas/Pets" 36 | default: 37 | description: unexpected error 38 | content: 39 | application/json: 40 | schema: 41 | $ref: "#/components/schemas/Error" 42 | post: 43 | summary: Create a pet 44 | operationId: createPets 45 | tags: 46 | - pets 47 | responses: 48 | '201': 49 | description: Null response 50 | default: 51 | description: unexpected error 52 | content: 53 | application/json: 54 | schema: 55 | $ref: "#/components/schemas/Error" 56 | /pets/{petId}: 57 | get: 58 | summary: Info for a specific pet 59 | operationId: showPetById 60 | tags: 61 | - pets 62 | parameters: 63 | - name: petId 64 | in: path 65 | required: true 66 | description: The id of the pet to retrieve 67 | schema: 68 | type: string 69 | responses: 70 | '200': 71 | description: Expected response to a valid request 72 | content: 73 | application/json: 74 | schema: 75 | $ref: "#/components/schemas/Pets" 76 | default: 77 | description: unexpected error 78 | content: 79 | application/json: 80 | schema: 81 | $ref: "#/components/schemas/Error" 82 | components: 83 | schemas: 84 | Pet: 85 | required: 86 | - id 87 | - name 88 | properties: 89 | id: 90 | type: integer 91 | format: int64 92 | name: 93 | type: string 94 | tag: 95 | type: string 96 | Pets: 97 | type: array 98 | items: 99 | $ref: "#/components/schemas/Pet" 100 | Error: 101 | required: 102 | - code 103 | - message 104 | properties: 105 | code: 106 | type: integer 107 | format: int32 108 | message: 109 | type: string 110 | -------------------------------------------------------------------------------- /_fixtures/tech_mapping.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inherd/coco/43ae3f3afd70705e0827d959e73efd89abc4a299/_fixtures/tech_mapping.yml -------------------------------------------------------------------------------- /coco.yml: -------------------------------------------------------------------------------- 1 | repos: 2 | - url: https://github.com/coco-rs/coco.fixtures 3 | languages: [Java] 4 | - url: https://github.com/coco-rs/coco.fixtures2 5 | - url: . 6 | languages: [Rust] 7 | - url: https://github.com/datum-lang/scie 8 | - url: https://github.com/projectfluent/fluent-rs 9 | languages: [Rust, JavaScript] 10 | 11 | plugins: 12 | - name: swagger 13 | - name: struct 14 | config: 15 | - key: ctags 16 | value: /usr/local/bin/ctags 17 | 18 | git: 19 | local: false 20 | 21 | # todo: api mapping services/repository 22 | openapi: 23 | - url: _fixtures/swagger/petstore.yaml 24 | version: 2.0 25 | 26 | # todo: add commit 27 | commit: 28 | # default: conventional commit: (?build)(?(?:\([^()\r\n]*\)|\()?(?!)?)(?:.*)? 29 | # link url: https://regex101.com/r/V5J8kh/1 30 | # 31 | # jira: ^(feature|fix)\/(([a-z,A-Z]+))(-)(\d*)(:)([a-z,0–9]) 32 | # jira test case: feature/JIR-124:test commit message 33 | regex: ^(feature|fix)/([a-z,A-Z]+-\d*):([a-z,0–9]) 34 | matches: 35 | - scope 36 | - tag 37 | - id 38 | samples: feature/JIR-124:test commit message 39 | -------------------------------------------------------------------------------- /core_model/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "core_model" 3 | version = "0.1.0" 4 | authors = ["Phodal Huang "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | lazy_static = "1.4.0" 11 | 12 | # serializing and deserializing 13 | serde = { version = "1.0", features = ["derive"] } 14 | serde_json = "1.0" 15 | serde_yaml = "0.8" # for config parse 16 | 17 | # parse for url 18 | url = "2.2.0" 19 | 20 | # handle for shell path like ~ 21 | shellexpand = "2.1.0" 22 | 23 | regex = "1" 24 | -------------------------------------------------------------------------------- /core_model/src/coco_settings.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use std::fs; 3 | use std::path::{Path, PathBuf}; 4 | 5 | lazy_static! { 6 | static ref COCO_CONFIG: HashMap<&'static str, &'static str> = { 7 | let mut m = HashMap::new(); 8 | m.insert("dir", ".coco"); 9 | m 10 | }; 11 | } 12 | 13 | /// a collections of dir for projects 14 | /// - `root()`, which is `.coco` 15 | /// - `reporter()`, which is `.coco/reporter` 16 | /// under `.coco/reporter` 17 | /// - `architecture()` 18 | /// - `git()` 19 | /// - `cloc()` 20 | /// - `framework()` 21 | pub struct Settings {} 22 | 23 | impl Settings { 24 | pub fn global_config(key: &str) -> &'static str { 25 | return COCO_CONFIG.get(key).unwrap(); 26 | } 27 | 28 | pub fn root() -> &'static str { 29 | return Settings::global_config("dir"); 30 | } 31 | 32 | pub fn reporter(child: Option<&str>) -> PathBuf { 33 | let root = Path::new(Settings::root()); 34 | let reporter_path = root.join("reporter"); 35 | if !reporter_path.exists() { 36 | let _ = fs::create_dir_all(&reporter_path); 37 | } 38 | 39 | match child { 40 | None => reporter_path, 41 | Some(str) => { 42 | let child_path = reporter_path.join(str); 43 | if !child_path.exists() { 44 | let _ = fs::create_dir_all(&child_path); 45 | } 46 | 47 | child_path 48 | } 49 | } 50 | } 51 | 52 | pub fn git() -> PathBuf { 53 | Settings::reporter(Some("git")) 54 | } 55 | 56 | pub fn cloc() -> PathBuf { 57 | Settings::reporter(Some("cloc")) 58 | } 59 | 60 | pub fn architecture() -> PathBuf { 61 | Settings::reporter(Some("architecture")) 62 | } 63 | 64 | pub fn framework() -> PathBuf { 65 | Settings::reporter(Some("framework")) 66 | } 67 | 68 | pub fn pipeline() -> PathBuf { 69 | Settings::reporter(Some("pipeline")) 70 | } 71 | 72 | pub fn struct_dir() -> PathBuf { 73 | Settings::reporter(Some("struct")) 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /core_model/src/coco_struct.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | #[derive(Serialize, Deserialize, Debug, Clone)] 4 | pub struct MemberInfo { 5 | pub name: String, 6 | pub access: String, 7 | pub data_type: String, 8 | pub pure_data_type: String, 9 | } 10 | 11 | impl MemberInfo { 12 | pub fn new(name: &str, access: &str, data_type: String) -> Self { 13 | MemberInfo { 14 | name: name.to_string(), 15 | access: access.to_string(), 16 | data_type, 17 | pure_data_type: "".to_string(), 18 | } 19 | } 20 | } 21 | 22 | #[derive(Serialize, Deserialize, Debug, Clone)] 23 | pub struct MethodInfo { 24 | pub name: String, 25 | pub access: String, 26 | pub parameters: Vec, 27 | pub return_type: String, 28 | pub pure_return_type: String, 29 | } 30 | 31 | impl MethodInfo { 32 | pub fn new(name: &str, access: &str, parameters: Vec, return_type: String) -> Self { 33 | MethodInfo { 34 | name: name.to_string(), 35 | access: access.to_string(), 36 | parameters, 37 | return_type, 38 | pure_return_type: "".to_string(), 39 | } 40 | } 41 | 42 | pub fn parameter_too_long(&self) -> bool { 43 | self.parameters.len() > 5 44 | } 45 | } 46 | 47 | #[derive(Serialize, Deserialize, Debug, Clone)] 48 | pub struct ClassInfo { 49 | pub name: String, 50 | pub id: i32, 51 | pub file: String, 52 | pub lang: String, 53 | pub parents: Vec, 54 | pub members: Vec, 55 | pub methods: Vec, 56 | } 57 | 58 | impl ClassInfo { 59 | pub fn new(class_name: &str) -> Self { 60 | ClassInfo { 61 | name: class_name.to_string(), 62 | id: 0, 63 | file: "".to_string(), 64 | lang: "".to_string(), 65 | parents: vec![], 66 | members: vec![], 67 | methods: vec![], 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /core_model/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate lazy_static; 3 | 4 | pub use coco_config::{CocoConfig, RepoConfig}; 5 | pub mod coco_config; 6 | 7 | pub use coco_settings::Settings; 8 | pub mod coco_settings; 9 | 10 | pub use plugin_interface::PluginInterface; 11 | pub mod plugin_interface; 12 | 13 | pub use support::url_format; 14 | pub mod coco_struct; 15 | pub mod support; 16 | -------------------------------------------------------------------------------- /core_model/src/plugin_interface.rs: -------------------------------------------------------------------------------- 1 | use crate::CocoConfig; 2 | 3 | pub trait PluginInterface { 4 | /// name of plugins 5 | /// should start with `coco.`, such as `coco.swagger` 6 | fn name(&self) -> &'static str; 7 | /// event for load plugin 8 | fn on_plugin_load(&self) {} 9 | /// event of unload plugin 10 | fn on_plugin_unload(&self) {} 11 | /// execute plugin 12 | fn execute(&self, config: CocoConfig); 13 | } 14 | -------------------------------------------------------------------------------- /core_model/src/support/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod path_format; 2 | pub mod url_format; 3 | -------------------------------------------------------------------------------- /core_model/src/support/path_format.rs: -------------------------------------------------------------------------------- 1 | use std::path::PathBuf; 2 | 3 | pub fn expand(path: &str) -> PathBuf { 4 | let input = &PathBuf::from(path).display().to_string(); 5 | let path = shellexpand::tilde(input); 6 | return PathBuf::from(path.to_string()); 7 | } 8 | 9 | #[cfg(test)] 10 | mod test { 11 | use crate::support::path_format::expand; 12 | 13 | #[test] 14 | fn format_path() { 15 | let string = expand("~"); 16 | assert_ne!(string.display().to_string(), "~"); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # 度量指标 2 | 3 | `config.co` ini 文件? 4 | 5 | ## 容器化成熟度 6 | 7 | 容器化平台 8 | 9 | ## 持续集成 10 | 11 | Jenkinsfile 识别? - 复杂度分析?根据长度? 12 | 13 | 支持自定义配置 14 | 15 | 提交与-持续集成时间对比(Selenium 机制?) 16 | 17 | ## 制品管理 18 | 19 | 自动化 tag 20 | 21 | 分析 tag 提交与发布 22 | 23 | ## PR or MR 分析 24 | 25 | ## 协作分析 26 | 27 | 人员数量、加入时间、频率变化、成熟度 28 | 29 | 平均团队时间 30 | 31 | ## 工具化 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /docs/adr/0001-metrics-model-design.md: -------------------------------------------------------------------------------- 1 | # 1. metrics model design 2 | 3 | 日期: 2021-01-10 4 | 5 | ## 状态 6 | 7 | 2021-01-10 提议 8 | 9 | ## 背景 10 | 11 | 在这里补充上下文... 12 | 13 | ## 决策 14 | 15 | 在这里补充上决策信息... 16 | 17 | ## 后果 18 | 19 | 在这里记录结果... 20 | -------------------------------------------------------------------------------- /docs/adr/0002-chart-library.md: -------------------------------------------------------------------------------- 1 | # 2. chart library 2 | 3 | Date: 2021-01-13 4 | 5 | ## Status 6 | 7 | 2021-01-13 proposed 8 | 9 | 2021-01-26 deprecated 10 | 11 | 2021-03-12 done 12 | 13 | ## Context 14 | 15 | We need a simple chart library by split. 16 | 17 | ## Decision 18 | 19 | [Plotters](https://github.com/38/plotters) - A rust drawing library for high quality data plotting for both WASM and native, statically and realtimely. 20 | 21 | ## Consequences 22 | 23 | Consequences here... 24 | -------------------------------------------------------------------------------- /docs/adr/0003-config-design.md: -------------------------------------------------------------------------------- 1 | # 3. config design 2 | 3 | Date: 2021-01-14 4 | 5 | ## Status 6 | 7 | 2021-01-14 proposed 8 | 9 | ## Context 10 | 11 | In order to support multiple project clone, we need to use a config for support it. 12 | 13 | ```yaml 14 | repo: 15 | - url: https://github.com/coco-rs/coco.fixtures 16 | - url: https://github.com/coco-rs/coco.fixtures2 17 | ``` 18 | 19 | ## Decision 20 | 21 | Decision here... 22 | 23 | ## Consequences 24 | 25 | Consequences here... 26 | -------------------------------------------------------------------------------- /docs/adr/0004-format-git-commit-message.md: -------------------------------------------------------------------------------- 1 | # 4. format git commit message 2 | 3 | Date: 2021-01-15 4 | 5 | ## Status 6 | 7 | 2021-01-15 proposed 8 | 9 | 2021-02-04 done 10 | 11 | ## Context 12 | 13 | Context here... 14 | 15 | ## Decision 16 | 17 | Decision here... 18 | 19 | ## Consequences 20 | 21 | Consequences here... 22 | -------------------------------------------------------------------------------- /docs/adr/0005-embedded-database.md: -------------------------------------------------------------------------------- 1 | # 5. embedded database 2 | 3 | Date: 2021-01-15 4 | 5 | ## Status 6 | 7 | 2021-01-15 proposed 8 | 9 | 2021-02-04 done 10 | 11 | ## Context 12 | 13 | Sqlite or SQLite 14 | 15 | - [sled](https://github.com/spacejam/sled) - the champagne of beta embedded databases 16 | - [Rusqlite](https://github.com/rusqlite/rusqlite) 17 | 18 | ## Decision 19 | 20 | Decision here... 21 | 22 | ## Consequences 23 | 24 | Consequences here... 25 | -------------------------------------------------------------------------------- /docs/adr/0006-tech-stack-generate.md: -------------------------------------------------------------------------------- 1 | # 6. tech stack generate 2 | 3 | Date: 2021-01-15 4 | 5 | ## Status 6 | 7 | 2021-01-15 proposed 8 | 9 | ## Context 10 | 11 | Context here... 12 | 13 | ## Decision 14 | 15 | Decision here... 16 | 17 | ## Consequences 18 | 19 | Consequences here... 20 | -------------------------------------------------------------------------------- /docs/adr/0007-regex-match-commit-message-design.md: -------------------------------------------------------------------------------- 1 | # 7. regex match commit message design 2 | 3 | Date: 2021-01-19 4 | 5 | ## Status 6 | 7 | 2021-01-19 proposed 8 | 9 | 2021-02-04 done 10 | 11 | ## Context 12 | 13 | Gitlab Docs: [https://docs.gitlab.com/ee/push_rules/push_rules.html](https://docs.gitlab.com/ee/push_rules/push_rules.html) 14 | 15 | Gitlab use RE2: [https://github.com/google/re2/wiki/Syntax](https://github.com/google/re2/wiki/Syntax) 16 | 17 | Rust RE2 18 | 19 | ```yaml 20 | commit-message: 21 | regex: (\d{4})-(\d{2})-(\d{2}) 22 | matches: 23 | - year 24 | - month 25 | - day 26 | ``` 27 | 28 | Conventional Commit: 29 | 30 | ```yaml 31 | commit-message: 32 | default: (?build)(?(?:\([^()\r\n]*\)|\()?(?!)?)(?:.*)? 33 | ``` 34 | 35 | Jira Samples 36 | 37 | ```yaml 38 | commit-message: 39 | regex: ^(feature|fix)\/(([a-z,A-Z]+))(-)(\d*)(:)([a-z,0–9]) 40 | matches: 41 | - branch 42 | - tag 43 | - id 44 | samples: feature/JIR-124:test commit message 45 | ``` 46 | 47 | Samples: 48 | 49 | ```yaml 50 | branch: 51 | regex: (^(develop|dev|master|revert-[a-z0-9-]+)|(feature/|bug/|hotfix/|release/)[a-z0-9-]+) 52 | matches: 53 | - branch 54 | - tag 55 | - id 56 | samples: 57 | ``` 58 | 59 | ## Decision 60 | 61 | Decision here... 62 | 63 | ## Consequences 64 | 65 | Consequences here... 66 | -------------------------------------------------------------------------------- /docs/adr/0008-virualenv-for-runtime.md: -------------------------------------------------------------------------------- 1 | # 8. virualenv for runtime 2 | 3 | Date: 2021-01-26 4 | 5 | ## Status 6 | 7 | 2021-01-26 proposed 8 | 9 | ## Context 10 | 11 | [Rustwide](https://github.com/rust-lang/rustwide) it supports for windows. 12 | 13 | Rustwide is a library to execute your code on the Rust ecosystem, powering projects like Crater and docs.rs. It features: 14 | 15 | ## Decision 16 | 17 | Decision here... 18 | 19 | ## Consequences 20 | 21 | Consequences here... 22 | -------------------------------------------------------------------------------- /docs/adr/0009-git-http-server.md: -------------------------------------------------------------------------------- 1 | # 9. git http server 2 | 3 | Date: 2021-02-03 4 | 5 | ## Status 6 | 7 | 2021-02-03 proposed 8 | 9 | 2021-03-12 done 10 | 11 | ## Context 12 | 13 | In the case, we need a HTTP Server for query different git information: 14 | 15 | 1. team summary 16 | - join team 17 | - commits in month 18 | 2. git changes relations 19 | 3. git branches summary 20 | 4. ... 21 | 22 | ## Decision 23 | 24 | Build a HTTP server, thinking in HTTP Server 25 | 26 | 1. with GraphQL: [juniper](https://github.com/graphql-rust/juniper) 27 | 2. with Sled: [https://github.com/spacejam/sled](https://github.com/spacejam/sled) 28 | 3. with SQLite: [https://github.com/rusqlite/rusqlite](https://github.com/rusqlite/rusqlite) 29 | 30 | ## Consequences 31 | 32 | Consequences here... 33 | -------------------------------------------------------------------------------- /docs/adr/0010-limit-git-changes-by-time.md: -------------------------------------------------------------------------------- 1 | # 10. limit git changes by time 2 | 3 | Date: 2021-02-04 4 | 5 | ## Status 6 | 7 | 2021-02-04 proposed 8 | 9 | 2021-03-12 done 10 | 11 | ## Context 12 | 13 | Summary of Git commits by projects without FileChange 14 | 15 | | Project Name | Project Commits | Time | Times(ms) | 16 | |------------------|-----------------|--------|-------------------| 17 | | Rust Regex | 1078 | 3s | 2919ms ~ 3012ms | 18 | | Lombok | 3127 | 8s | 8096ms ~ 8616ms | 19 | | Nginx | 6805 | 32s | 32468ms ~ 33967ms | 20 | | Redis | 10009 | 67s | 65328ms ~ 71616ms | 21 | | Spring Framework | 22133 | 706s | | 22 | | Graal | 49026 | 1425s | | 23 | | Gradle | 78711 | 4130s | | 24 | 25 | If one project had lot of projects will be slowly. 26 | 27 | ## Decision 28 | 29 | We can support git commits number. 30 | 31 | ## Consequences 32 | 33 | Consequences here... 34 | -------------------------------------------------------------------------------- /docs/adr/0011-architecture-analysis.md: -------------------------------------------------------------------------------- 1 | # 11. architecture analysis 2 | 3 | Date: 2021-02-04 4 | 5 | ## Status 6 | 7 | 2021-02-04 proposed 8 | 9 | ## Context 10 | 11 | basic analysis 12 | 13 | - deployment architecture analysis 14 | - pipeline analysis 15 | - JenkinsFile 16 | - data analysis 17 | - SQL analysis? 18 | - struct name analysis? 19 | - java model/controller? 20 | 21 | DSL logger 22 | 23 | - log for DSL parser and visual 24 | - IFML 25 | - VizDSL 26 | 27 | Integration 28 | 29 | - Swagger Parser 30 | - [OpenAPI](https://github.com/softprops/openapi). Rust crate for serializing and deserializing open api documents 31 | 32 | 33 | Samples 34 | 35 | - Data 36 | - Controller 37 | - Model 38 | - Action 39 | - Paramter Binding 40 | - ViewComponent 41 | - ViewController 42 | - Layout 43 | 44 | From: Software Architecture Evaluation: A Systematic Mapping Study 45 | 46 | Contribution Type 47 | 48 | - Domain Model 49 | - Framework 50 | - Method 51 | - Model 52 | - Process 53 | - Tool 54 | 55 | Empirical Type 56 | 57 | - Case Study 58 | - Survey 59 | - Experiment 60 | - Theory 61 | - Other 62 | 63 | ## Decision 64 | 65 | Decision here... 66 | 67 | ## Consequences 68 | 69 | Consequences here... 70 | -------------------------------------------------------------------------------- /docs/adr/0012-file-change-count.md: -------------------------------------------------------------------------------- 1 | # 12. file change count 2 | 3 | Date: 2021-02-05 4 | 5 | ## Status 6 | 7 | 2021-02-05 proposed 8 | 9 | ## Context 10 | 11 | Context here... 12 | 13 | ## Decision 14 | 15 | 1. 在 commit 中存储数据 16 | 2. 如果单次提交修改数过多,如 10 次,则视为重构,忽略变更 17 | 18 | ## Consequences 19 | 20 | Consequences here... 21 | -------------------------------------------------------------------------------- /docs/adr/0013-suggest-design.md: -------------------------------------------------------------------------------- 1 | # 13. suggest design 2 | 3 | Date: 2021-02-07 4 | 5 | ## Status 6 | 7 | 2021-02-07 proposed 8 | 9 | ## Context 10 | 11 | Context here... 12 | 13 | ## Decision 14 | 15 | We still need some key information from users, which we maybe cannot get by automatic. So in Coco, we need a webpage to 16 | interact with user to get more info. such as : 17 | 18 | 1. deploy architecture with DSL design 19 | 2. data flow or data tool 20 | 3. Organization Description Languages 21 | 22 | ## Consequences 23 | 24 | Consequences here... 25 | -------------------------------------------------------------------------------- /docs/adr/0014-organization-description-languages.md: -------------------------------------------------------------------------------- 1 | # 14. Organization Description Languages 2 | 3 | Date: 2021-02-07 4 | 5 | ## Status 6 | 7 | 2021-02-07 proposed 8 | 9 | ## Context 10 | 11 | Context here... 12 | 13 | ## Decision 14 | 15 | Decision here... 16 | 17 | ## Consequences 18 | 19 | Consequences here... 20 | -------------------------------------------------------------------------------- /docs/adr/0015-struct-analysis.md: -------------------------------------------------------------------------------- 1 | # 15. struct analysis 2 | 3 | Date: 2021-02-07 4 | 5 | ## Status 6 | 7 | 2021-02-07 proposed 8 | 9 | 2021-02-24 done 10 | 11 | ## Context 12 | 13 | If we want to do some module analysis, we can use `Ctags` to analysis source code. 14 | 15 | related-Rust project: [https://github.com/dan-t/rusty-tags](https://github.com/dan-t/rusty-tags) 16 | 17 | ## Decision 18 | 19 | 1. use ctags for analysis 20 | 21 | ## Consequences 22 | 23 | Consequences here... 24 | -------------------------------------------------------------------------------- /docs/adr/0017-usr-cargo-make-for-custom-tasks-sets.md: -------------------------------------------------------------------------------- 1 | # 17. usr cargo-make for custom tasks sets 2 | 3 | Date: 2021-02-08 4 | 5 | ## Status 6 | 7 | 2021-02-08 proposed 8 | 9 | ## Context 10 | 11 | Context here... 12 | 13 | ## Decision 14 | 15 | Decision here... 16 | 17 | ## Consequences 18 | 19 | Consequences here... 20 | -------------------------------------------------------------------------------- /docs/adr/0018-cocomo-analysis-plugin.md: -------------------------------------------------------------------------------- 1 | # 18. COCOMO analysis plugin 2 | 3 | Date: 2021-02-10 4 | 5 | ## Status 6 | 7 | 2021-02-10 proposed 8 | 9 | 2021-03-12 deprecated 10 | 11 | ## Context 12 | 13 | COCOMO (Constructive Cost Model) 14 | 15 | - E=ab(KLOC)bb 16 | - D=cb(E)db 17 | - P=E/D 18 | 19 | use [scc](https://github.com/boyter/scc) to create cocomo. 20 | 21 | ## Decision 22 | 23 | Decision here... 24 | 25 | ## Consequences 26 | 27 | Consequences here... 28 | -------------------------------------------------------------------------------- /docs/adr/0019-read-code-coverage.md: -------------------------------------------------------------------------------- 1 | # 19. read code coverage 2 | 3 | Date: 2021-02-14 4 | 5 | ## Status 6 | 7 | 2021-02-14 proposed 8 | 9 | 2021-03-12 deprecated 10 | 11 | ## Context 12 | 13 | Code coverage is a way for fitness, we need to collect test information in our projects. 14 | 15 | - GCC or LLVM/Clang, with `.profraw` `.gcda` file 16 | - JavaScript with `.lcov` file 17 | - Java with `JaCoCo` 18 | 19 | ## Decision 20 | 21 | 1. Use [Grcov](https://github.com/mozilla/grcov) to collects and aggregates code coverage information. 22 | 2. Try integration it, if size it big, split as plugins. 23 | 24 | ## Consequences 25 | 26 | Consequences here... 27 | -------------------------------------------------------------------------------- /docs/adr/0020-output-for-plugins.md: -------------------------------------------------------------------------------- 1 | # 20. output for plugins 2 | 3 | Date: 2021-02-18 4 | 5 | ## Status 6 | 7 | 2021-02-18 proposed 8 | 9 | 2021-02-24 done 10 | 11 | ## Context 12 | 13 | In order to get output in better way, we need to set output dir to someway to some ways. 14 | 15 | ## Decision 16 | 17 | - use `dest/plugins` for save plugins output 18 | - use `dest` for other artifacts 19 | - compressed output plugins for save 20 | 21 | ## Consequences 22 | 23 | Consequences here... 24 | -------------------------------------------------------------------------------- /docs/adr/0021-plugin-config.md: -------------------------------------------------------------------------------- 1 | # 21. plugin config 2 | 3 | Date: 2021-02-18 4 | 5 | ## Status 6 | 7 | 2021-02-18 proposed 8 | 9 | 2021-02-24 done 10 | 11 | ## Context 12 | 13 | Context here... 14 | 15 | ## Decision 16 | 17 | - ~~use CLI to generate `plugins.yml` to list plugins config~~ 18 | - ~~in default, we use all plugins~~ 19 | - use `coco.yml` to read plugins 20 | 21 | ## Consequences 22 | 23 | Consequences here... 24 | -------------------------------------------------------------------------------- /docs/adr/0022-suggest-with-i18n.md: -------------------------------------------------------------------------------- 1 | # 22. suggest with i18n 2 | 3 | Date: 2021-02-20 4 | 5 | ## Status 6 | 7 | 2021-02-20 proposed 8 | 9 | ## Context 10 | 11 | Context here... 12 | 13 | ## Decision 14 | 15 | examples: https://github.com/rust-lang/www.rust-lang.org 16 | 17 | with: 18 | 19 | - handlebars 20 | - handlebars-fluent 21 | - fluent-syntax 22 | - fluent-bundle 23 | - fluent-locale 24 | - fluent 25 | 26 | ## Consequences 27 | 28 | Consequences here... 29 | -------------------------------------------------------------------------------- /docs/adr/0023-psa(project-structure-analysis).md: -------------------------------------------------------------------------------- 1 | # 23. PSA (Project structure analysis) 2 | 3 | Date: 2021-02-22 4 | 5 | ## Status 6 | 7 | 2021-02-22 proposed 8 | 9 | ## Context 10 | 11 | ### Concepts 12 | 13 | 1. Project, The project consists of modules, a single-module or a multi-module project is possible. 14 | 2. Module, The module consists of build scripts, unit tests, etc. 15 | 3. Content roots, The directories where the files belonging to the module. 16 | 4. Facet, The facets represents specific for a particular framework/technology, associated with a module. 17 | 5. Library, The library is an archive of compiled code that modules depend on. 18 | 19 | ### Relationship of concepts 20 | 21 | ![](../images/psa.svg) 22 | 23 | ## Decision 24 | 25 | ## Consequences 26 | -------------------------------------------------------------------------------- /docs/adr/0025-deploy-analyser.md: -------------------------------------------------------------------------------- 1 | # 25. deploy analyser 2 | 3 | Date: 2021-02-25 4 | 5 | ## Status 6 | 7 | 2021-02-25 proposed 8 | 9 | ## Context 10 | 11 | Context here... 12 | 13 | ## Decision 14 | 15 | ### Dockerfile 分析 16 | 17 | Rust 版本: 18 | 19 | https://github.com/HewlettPackard/dockerfile-parser-rs/ 20 | 21 | Python 版本: 22 | 23 | https://github.com/containerbuildsystem/dockerfile-parse 24 | https://github.com/asottile/dockerfile 25 | 26 | 27 | ## Consequences 28 | 29 | Consequences here... 30 | -------------------------------------------------------------------------------- /docs/adr/0026-use-plantuml-for-convert-uml.md: -------------------------------------------------------------------------------- 1 | # 26. use plantuml for convert uml 2 | 3 | Date: 2021-02-27 4 | 5 | ## Status 6 | 7 | 2021-02-27 proposed 8 | 9 | 2021-03-12 done 10 | 11 | ## Context 12 | 13 | Context here... 14 | 15 | ## Decision 16 | 17 | Decision here... 18 | 19 | ## Consequences 20 | 21 | Consequences here... 22 | -------------------------------------------------------------------------------- /docs/adr/0027-dockefile-analysis.md: -------------------------------------------------------------------------------- 1 | # 27. dockefile analysis 2 | 3 | Date: 2021-03-02 4 | 5 | ## Status 6 | 7 | 2021-03-02 proposed 8 | 9 | ## Context 10 | 11 | [Dive](https://github.com/wagoodman/dive) A tool for exploring each layer in a docker image. 12 | 13 | [Release: Static analysis for Dockerfile](https://deepsource.io/blog/release-dockerfile-static-analysis/) 14 | 15 | **Security issues:** 16 | 17 | - Last user should not be `root` 18 | - Use only an allowed registry in the `FROM` image 19 | 20 | **Bug risks:** 21 | 22 | - `COPY --from` should reference a previously defined `FROM` alias 23 | - Multiple `ENTRYPOINT` instructions detected 24 | - Multiple `CMD` instructions detected 25 | 26 | **Performance issues:** 27 | 28 | - Use `COPY` instead of `ADD` for files and folders 29 | - Use `ADD` for extracting archives into an image 30 | - Delete the `apt-get` lists after installing something 31 | 32 | ## Decision 33 | 34 | Decision here... 35 | 36 | ## Consequences 37 | 38 | Consequences here... 39 | -------------------------------------------------------------------------------- /docs/adr/0028-kubernetes-analysis.md: -------------------------------------------------------------------------------- 1 | # 28. kubernetes analysis 2 | 3 | Date: 2021-03-02 4 | 5 | ## Status 6 | 7 | 2021-03-02 proposed 8 | 9 | ## Context 10 | 11 | Related: [KubeLinter](https://github.com/stackrox/kube-linter) 12 | 13 | ## Decision 14 | 15 | Decision here... 16 | 17 | ## Consequences 18 | 19 | Consequences here... 20 | -------------------------------------------------------------------------------- /docs/adr/0029-integration-openapi-source-code.md: -------------------------------------------------------------------------------- 1 | # 29. integration openapi source code 2 | 3 | Date: 2021-03-09 4 | 5 | ## Status 6 | 7 | 2021-03-09 proposed 8 | 9 | 2021-03-12 done 10 | 11 | ## Context 12 | 13 | Context here... 14 | 15 | ## Decision 16 | 17 | Decision here... 18 | 19 | ## Consequences 20 | 21 | Consequences here... 22 | -------------------------------------------------------------------------------- /docs/adr/0030-extract-infrastructure-mod.md: -------------------------------------------------------------------------------- 1 | # 30. extract infrastructure mod 2 | 3 | Date: 2021-03-10 4 | 5 | ## Status 6 | 7 | 2021-03-10 proposed 8 | 9 | ## Context 10 | 11 | Context here... 12 | 13 | ## Decision 14 | 15 | Decision here... 16 | 17 | ## Consequences 18 | 19 | Consequences here... 20 | -------------------------------------------------------------------------------- /docs/adr/0031-markdown-for-terminal-output.md: -------------------------------------------------------------------------------- 1 | # 31. markdown for terminal output 2 | 3 | Date: 2021-03-17 4 | 5 | ## Status 6 | 7 | 2021-03-17 proposed 8 | 9 | ## Context 10 | 11 | Context here... 12 | 13 | ## Decision 14 | 15 | Decision here... 16 | 17 | ## Consequences 18 | 19 | Consequences here... 20 | -------------------------------------------------------------------------------- /docs/adr/README.md: -------------------------------------------------------------------------------- 1 | # Architecture Decision Records 2 | 3 | * [1. metrics-model-design](0001-metrics-model-design.md) 4 | * [2. chart-library](0002-chart-library.md) 5 | * [3. config-design](0003-config-design.md) 6 | * [4. format-git-commit-message](0004-format-git-commit-message.md) 7 | * [5. embedded-database](0005-embedded-database.md) 8 | * [6. tech-stack-generate](0006-tech-stack-generate.md) 9 | * [7. regex-match-commit-message-design](0007-regex-match-commit-message-design.md) 10 | * [8. virualenv-for-runtime](0008-virualenv-for-runtime.md) 11 | * [9. git-http-server](0009-git-http-server.md) 12 | * [10. limit-git-changes-by-time](0010-limit-git-changes-by-time.md) 13 | * [11. architecture-analysis](0011-architecture-analysis.md) 14 | * [12. file-change-count](0012-file-change-count.md) 15 | * [13. suggest-design](0013-suggest-design.md) 16 | * [14. organization-description-languages](0014-organization-description-languages.md) 17 | * [15. struct-analysis](0015-struct-analysis.md) 18 | * [16. plugin-system-design](0016-plugin-system-design.md) 19 | * [17. usr-cargo-make-for-custom-tasks-sets](0017-usr-cargo-make-for-custom-tasks-sets.md) 20 | * [18. cocomo-analysis-plugin](0018-cocomo-analysis-plugin.md) 21 | * [19. read-code-coverage](0019-read-code-coverage.md) 22 | * [20. output-for-plugins](0020-output-for-plugins.md) 23 | * [21. plugin-config](0021-plugin-config.md) 24 | * [22. suggest-with-i18n](0022-suggest-with-i18n.md) 25 | * [23. psa(project-structure-analysis)](0023-psa(project-structure-analysis).md) 26 | * [24. jenkinsfile-parser](0024-jenkinsfile-parser.md) 27 | * [25. deploy-analyser](0025-deploy-analyser.md) 28 | * [26. use-plantuml-for-convert-uml](0026-use-plantuml-for-convert-uml.md) 29 | * [27. dockefile-analysis](0027-dockefile-analysis.md) 30 | * [28. kubernetes-analysis](0028-kubernetes-analysis.md) 31 | * [29. integration-openapi-source-code](0029-integration-openapi-source-code.md) 32 | * [30. extract-infrastructure-mod](0030-extract-infrastructure-mod.md) 33 | * [31. markdown-for-terminal-output](0031-markdown-for-terminal-output.md) 34 | -------------------------------------------------------------------------------- /docs/case-study/README.md: -------------------------------------------------------------------------------- 1 | # Find some case 2 | 3 | [Analysing source control history with Rust](https://medium.com/thg-tech-blog/analysing-source-control-history-with-rust-ba766cf1f648) 4 | 5 | -------------------------------------------------------------------------------- /docs/coco_sample.yml: -------------------------------------------------------------------------------- 1 | repos: 2 | - url: . 3 | languages: [Rust] 4 | 5 | 6 | plugins: 7 | - name: swagger 8 | - name: struct 9 | config: 10 | - key: ctags 11 | value: /usr/local/bin/ctags 12 | 13 | git: 14 | local: false 15 | -------------------------------------------------------------------------------- /docs/commands.md: -------------------------------------------------------------------------------- 1 | # Git Cli 2 | 3 | ## Commits per author 4 | 5 | ``` 6 | git shortlog -s -n 7 | ``` 8 | 9 | without merge 10 | 11 | ``` 12 | git shortlog -sn --no-merges 13 | ``` 14 | 15 | ## Tags 16 | 17 | list tags with refs 18 | 19 | ``` 20 | git show-ref --tags -d 21 | ``` 22 | 23 | with date 24 | 25 | ``` 26 | git log --tags --simplify-by-decoration --pretty="format:%t %at %d" 27 | ``` 28 | -------------------------------------------------------------------------------- /docs/model.md: -------------------------------------------------------------------------------- 1 | # 模型 2 | 3 | 4 | - 交付价值? 5 | - 响应速度 6 | - 产能 7 | - 沟通成本 8 | - 人员投入程度 9 | - 系统复杂度 10 | - 多项目修改 11 | - 质量 12 | - 能力 13 | 14 | 15 | ## 基于引用的复杂度 16 | 17 | ——《软件之道:软件开发争议问题剖析》 18 | 19 | | 指标 | 理想值 | FreeBSD | Linux | Solaris | WRK | 20 | |-----------------|---------|-----------|-----------|-----------|-----------| 21 | | 每个目录的文件数 | | 6.8 | 20.4 | 8.9 | 15.9 | 22 | | C 源文件的头文件数 | | 1.05 | 1.96 | 1.09 | 1.92 | 23 | |文件的平均结构复杂度 | | 2.2x10^14 | 1.3x10^13 | 5.4x19^12 | 2.6x10^13 | 24 | 25 | 文件的平均结构复杂度 = (被引用数 x 引用数) ^ 2 26 | -------------------------------------------------------------------------------- /docs/related.md: -------------------------------------------------------------------------------- 1 | # Related Projects 2 | 3 | 4 | ## [Gitinspector](https://github.com/ejwa/gitinspector) 5 | 6 | 📊 The statistical analysis tool for git repositories 7 | 8 | Features: 9 | 10 | - Shows cumulative work by each author in the history. 11 | - Filters results by extension (default: java,c,cc,cpp,h,hh,hpp,py,glsl,rb,js,sql). 12 | - Can display a statistical timeline analysis. 13 | - Scans for all filetypes (by extension) found in the repository. 14 | - Multi-threaded; uses multiple instances of git to speed up analysis when possible. 15 | - Supports HTML, JSON, XML and plain text output (console). 16 | - Can report violations of different code metrics. 17 | 18 | 19 | ## Hercules 20 | 21 | [Hercules](https://github.com/src-d/hercules) Gaining advanced insights from Git repository history. w 22 | 23 | powered by [go-git](https://github.com/go-git/go-git) 24 | 25 | Metrics 26 | 27 | - Code ownership 28 | - Couples (Architecture) 29 | - Efforts through time 30 | - Couples analysis automatically loads "shotness" data if available. 31 | - Aligned commit series 32 | - Added vs changed lines through time 33 | - Sentiment (positive and negative comments) 34 | 35 | Code ownership 36 | 37 | ![Code Ownership](https://raw.githubusercontent.com/src-d/hercules/master/doc/emberjs_people.png) 38 | 39 | 40 | ## Paid 41 | 42 | https://eazybi.com/integrations/git 43 | 44 | -------------------------------------------------------------------------------- /docs/social/coco-intro.md: -------------------------------------------------------------------------------- 1 | # 写个取代自己的工具:Coco —— 自动化分析与建议 2 | 3 | 作为一个资深的软件工程师,我经常遇到其他/她开发人员大量的重复问题。过去只靠写博客,现在,我有了四种方式来解决: 4 | 5 | - 博客。我的博客 phodal.com 上有 850+ 的博客 6 | - 工具。创造开源工具解决重复性问题,如:ADR、Lemonj、Coca、Clij 7 | - 开源电子书。系统性的归纳某一个领域相关实践和模式,如:《Serverless 应用架构》 8 | - 知识平台。结合工具和电子书,如 DevOps 知识平台:Ledge 9 | 10 | 即使如此,依旧没有解决一个问题:我需要人力来分析项目、再抛出这些链接。于是,过去我一直就在想:**能否做一个工具来取代自己?** 当然了,我的真实意思不是:取代我自己,而是取代我做的那些重复性活动。(PS:等写完之后,再写一个自动化写 PPT 的工具,就完美了。) 11 | 12 | 所以,我开始编写一个新的工具,一个关于对代码进行自动化分析与建议的工具。 13 | 14 | ## Coco:自动化分析与建议工具 15 | 16 | 在 Coco 的 README ( [https://github.com/phodal/coco](https://github.com/phodal/coco) )里,可以看到现在规划的 1.0 的相关的 Todo 列表。从某种意义上来说,这是一个 AI 工具(专家系统),它依赖于资深工程师的大量的经验。它的难度主要在于: 17 | 18 | - 工具的 MVP 版本。验证工具在技术上是可行的(PS:从我的角度来看,它并不存在问题) 19 | - 持续性的经验输入。持续完善整个工具的建议体系和架构 20 | - 上下游生态完善。获取上下游工具相关的资料和数据(PS:如 DevOps、云原生相关) 21 | - 避免功能膨胀。必要的情况下,通过插件的方式来扩展功能 22 | 23 | ### Coco 与 Coca 的关系 24 | 25 | [Coca](https://github.com/phodal/coca) 是笔者(@phodal)写的一个用于系统重构、系统迁移和系统分析的瑞士军刀。它可以分析代码中的测试坏味道、模块化分析、行数统计、分析调用与依赖、Git 分析以及自动化重构等。 26 | 27 | Coco 这个名字的来源是**椰子鸡**,正如 Coca 项目([https://github.com/phodal/coca](https://github.com/phodal/coca) )的来源是 Coca Cola,只是维度上由喝的变成吃的而已。Coco 是 Coca 的姊妹工具,与 Coca 工具是相互补充。 28 | 29 | ### Coco:自动化分析与建议工具 30 | 31 | Coco 要实现的主要功能是: 32 | 33 | - 分析。对项目进行全面化的分析,如 Git 历史、模块化分析、框架使用等 34 | - 报告。以可视化和文档的形式输出项目的总览信息(结合 D3 可视化的形式) 35 | - 建议。针对于项目中的问题,进行自动化的建议 36 | - 成熟度。(TBC,待定) 37 | 38 | 从里程碑来说,我们所要做的功能特别多。而结合 Coca 和 DevOps 知识平台 Ledge 在开源社区经受了一年多的洗礼,它们受到了越来越多的开发者的喜爱。与此同时,在这一年多的时间里,我也将自己的经验不断也输入到了项目中。 39 | 40 | 与此同时,我们将先创建一个组织:**Inherd**,作为这一系列工具的核心团队。 41 | 42 | ### Coco 技术栈 43 | 44 | 作为一个 CLI 工具,我本该选取 Golang 作为主要技术栈的。但是,经常与 Ledge 的开发者们讨论一番后,大家决定使用 Rust(虽然大部分人都没有经验)作为主要语言。与此同时,作为一个已经使用了一年的 Rust 语言的开发者,我觉得这并不是太大的难题。与此同时,这个工具未来也将在浏览器上运行,Rust 的 Web Assembly 支持比几大主流语言友好。 45 | 46 | 除了 Rust 之外,我们还需要可视化相关的报告等,为此我们还需要前端相关技术栈的开发,如 D3.js。当然了,能结合 Web Assembly + D3.js + 其它框架也是一个不错选项。 47 | 48 | ### Coco 进度 49 | 50 | 对于写一个工具来说,最难的是开个头,随后就是补充功能和重构了,谁都能做。 51 | 52 | 当前主要进展: 53 | 54 | - 使用 `libgit2` 实现对 Git 相关的分析中。 55 | - 集成 Tokei 实现 CLOC 相关的行数统计。 56 | - 集成 Scie 的框架检测功能,对技术栈进行可视化 57 | 58 | 以下主要模块还未开始: 59 | 60 | - 模块化分析 61 | - 可视化报告 62 | - 标签生成(AI,分词) 63 | - 改进建议 64 | - 工具建议 65 | 66 | 还有其它诸如案例学习等等,详细见项目的 README。 67 | 68 | ## 其它 69 | 70 | 欢迎加入我们:https://github.com/phodal/coco 71 | -------------------------------------------------------------------------------- /docs/thesis/arch-mapping.md: -------------------------------------------------------------------------------- 1 | # A systematic mapping study on the combination of software architecture and agile development 2 | 3 | specific activities 4 | 5 | - Architectural Analysis (AA) is aimed at defining the problems an architecture must solve. The outcome of this activity is a set of architecturally significant requirements (ASRs) (Hofmeister et al., 2007). 6 | - Architectural Synthesis (AS) proposes candidate architecture solutions to address the ASRs collected in AA, thus this activity moves from the problem to the solution space (Hofmeister et al., 2007). 7 | - Architectural Evaluation (AE) ensures that the architectural design decisions made are the right ones, and the candidate archi- tectural solutions proposed in AS are measured against the ASRs collected in AA (Hofmeister et al., 2007). 8 | - Architectural Implementation (AI) realizes the architecture by creating a detailed design (Tang et al., 2010). 9 | - Architectural Maintenance and Evolution (AME): Architectural maintenance is to change an architecture for the purpose of fix- ing faults (ISO, 2006, ISO, 2011) and architectural evolution is to respond to new requirements at architectural level (Postma et al., 2004). In this SMS, we simply considered architectural mainte- nance and architectural evolution as one activity, in which an ar- chitecture is changed either to fix faults or to implement new re- quirements. 10 | 11 | 12 | general activities 13 | 14 | - Architectural Recovery (AR) is used to extract the current architecture of a system from the system’s implementation (Medvidovic and Jakobac, 2006). 15 | - Architectural Description (ADp) is used to describe the architec- ture with a set of architectural elements (e.g., architecture views). This activity can help stakeholders (e.g., architects) to understand the system, and improve the communication and cooperation among stakeholders (ISO, 2011). 16 | - Architectural Understanding (AU) is used to comprehend the ar- chitectural elements (e.g., architectural decisions) and their rela- tionships in an architecture design (Li et al., 2013). 17 | - Architectural Impact Analysis (AIA) is used to identify the ar- chitectural elements, which are affected by a change scenario (Bengtsson et al., 2004). The analysis results include the compo- nents in architecture that are affected directly, as well as the indi- rect effects of changes to the architecture (Bengtsson et al., 2004). 18 | - Architectural Reuse (ARu) aims at reusing existing architectural design elements, such as architecture frameworks, decisions, and patterns in the architecture of a new system (IEEE, 2010). 19 | - Architectural Refactoring (ARf) aims at improving the architec- tural structure of a system without changing its external behavior (Babar et al., 2013; Fowler et al., 1999). 20 | 21 | 22 | -------------------------------------------------------------------------------- /docs/thesis/gitana.md: -------------------------------------------------------------------------------- 1 | # [Gitana](https://github.com/SOM-Research/Gitana) 2 | 3 | Download URL: [https://hal.inria.fr/hal-01187769/document](https://hal.inria.fr/hal-01187769/document) 4 | 5 | Git conceptual schema. 6 | 7 | ``` 8 | Repository 9 | - name : String 10 | - / filesDeleted : Int 11 | - / emptyLines : Int 12 | 13 | Reference 14 | - name : String 15 | - inRemote : Boolean 16 | 17 | Commit 18 | - sha : String 19 | - message : String 20 | - size : int 21 | - authored_date : Timestamp 22 | - committed_date : Timestamp 23 | 24 | Developer 25 | - name : String 26 | - email : String 27 | - / commitsPerMonth : Int 28 | - / filesDeleted : Int 29 | 30 | / LineDetail 31 | - type : String 32 | - lineNumber : Int 33 | - isCommented : Boolean 34 | - isPartiallyCommented : Boolean 35 | - isEmpty : Boolean 36 | - content : String 37 | 38 | FileModification 39 | - status : String 40 | - additions : int 41 | - deletions : int 42 | - changes : int 43 | - patch : String 44 | 45 | File 46 | - name : String 47 | - extension : String 48 | - getVersion(Date) : String 49 | ``` 50 | -------------------------------------------------------------------------------- /docs/thesis/team-first.md: -------------------------------------------------------------------------------- 1 | 版权归作者所有,任何形式转载请联系作者。 2 | 作者:天外非先(来自豆瓣) 3 | 来源:https://book.douban.com/review/12322626/ 4 | 5 | 6 | Team First? 7 | 8 | - 团队是一群人组成的,团队的绩效取决于一群人的协作,而不是每个人相加。 9 | - 每个人都有Cognitive Load的上限,团队也一样。思考团队的本身的特质,优势;团队完成目标的外部依赖。 10 | - 协作效率和团队规模相关。书中引用了很多心理学和社会学的理论来解释,信任关系是其中最重要的一个决定因素,很多书籍专门对此做了阐述。业界推荐的智力工作型团队的人数5-8人,大团队(group of teams)人数不应超过50人,大部门人数150-500人。(参考Denbar's number) 11 | 12 | 13 | Tuckman Teal Performance Model (以下为个人理解,非翻译) 14 | 15 | - 招募期(Forming)。集结; 16 | - 磨合期(Storming)。 团队成员相互熟悉彼此。这个时期每个人按照自己的习惯的方式工作,在团队中寻找适合自己的位置。 17 | - 成长期(Norming)。团队形成自己的工作方式(文化)。每个人开始在自己的角色位置上成长 18 | - 高产期(Performing)。不论是技术积累,还是团队文化,合作沟通模式,达到了非常高的水平。团队进入高产出时期。 19 | 20 | 这个模式和温伯格的团队模式互相呼应。个人认为,这个模型会在团队发展中螺旋式重复,在IT领域,技术的更新换代,使得团队不断的重复该过程。 21 | 22 | 版权归作者所有,任何形式转载请联系作者。 23 | 作者:天外非先(来自豆瓣) 24 | 来源:https://book.douban.com/review/12324181/ 25 | 26 | 27 | 书中介绍了四种典型团队 28 | 29 | - Stream-aligned。组织中最常见的团队。负责研发用户产品的主力部队。该团队的产出决定了公司提供给最终用户的价值。其他团队都是围绕怎么支持Steam-aligned的团队需求来组建的 —— 如何减少cognitive load。 30 | (对于Stream的理解。Stream就是用户value在产品中从设计到交付的一个过程/流/周期。Stream-aligned 的这种团队设计方式,有利于团队对最终交付负责,并得到用户反馈,持续改进提供给用户的价值。) 31 | - Enabling。专家顾问团队。比方说,一个新功能需要采用最新的加密技术,原有的Stream-aligned团队没有相关技术积累。专家顾问团队要负责将该技术引入,并负责协助具体的实现。 32 | - Platform。这个比较容易理解。platform团队负责实现平台的抽象层,屏蔽底层细节。让Steam-aligned的团队专注在业务开发上。 33 | - Complicated sub-system。Complicated subsystem 团队负责某个具体核心技术的实现和演进,这是和enabling团队的区别。图像识别和处理是某个产品的核心竞争力。而图像识别技术底层算法的优化与实现,这个需要专业的领域知识。成立一个专门的团队负责提供该模块给其他团队使用。既减轻了其他团队的负担,有利于业务逻辑的快速开发,又保持了产品核心技术竞争力。 34 | 35 | 在实际情况中,我们会看到不一样的团队职责划分。所以理解这四种团队的划分目的是非常重要的——Team First的思想里,很重要的两点就是:cognitive load和Trust (team size)。这种划分方式的主要目的是保证主力部队在正面战场能够 36 | 37 | 快速。Platform提供好用的基础设施。 38 | 灵活。Enabling 团队协助采用新技术。 39 | 武器精良。Complicated subsystem提供最先进的武器。即装即用。 40 | 41 | 这样理解四种团队的合作模式就很容易了。 42 | -------------------------------------------------------------------------------- /e2e/.gitignore: -------------------------------------------------------------------------------- 1 | default 2 | -------------------------------------------------------------------------------- /e2e/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "e2e" 3 | version = "0.1.0" 4 | authors = ["Phodal Huang "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | assert_cmd = "1.0.2" 11 | 12 | [dev-dependencies] 13 | walkdir = "2" 14 | -------------------------------------------------------------------------------- /e2e/_fixtures/coco-fixtures.yml: -------------------------------------------------------------------------------- 1 | repos: 2 | - url: https://github.com/coco-rs/coco.fixtures 3 | -------------------------------------------------------------------------------- /e2e/_fixtures/no-plugin.yml: -------------------------------------------------------------------------------- 1 | repos: 2 | - url: https://github.com/coco-rs/coco.fixtures2 3 | -------------------------------------------------------------------------------- /framework/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "framework" 3 | version = "0.2.0" 4 | authors = ["Phodal Huang "] 5 | edition = "2018" 6 | license = "MIT" 7 | readme = "README.md" 8 | repository = "https://github.com/phodal/coco" 9 | documentation = "https://github.com/phodal/coco" 10 | homepage = "https://github.com/phodal/coco" 11 | description = """ 12 | Framework is a detector for different frameworks in one projects 13 | """ 14 | categories = ["text-processing", "command-line-interface", "development-tools"] 15 | exclude = [ 16 | "benchmark/*", 17 | "fixtures/*", 18 | ".github/*", 19 | ".gitattributes", 20 | ".adr.json", 21 | ] 22 | 23 | [dependencies] 24 | serde = { version = "1.0", features = ["derive"] } 25 | serde_derive = "1.0.115" 26 | serde_json = "1.0" 27 | erased-serde = "0.3" 28 | 29 | lazy_static = "1.2" 30 | 31 | walkdir = "2" 32 | 33 | regex = "1" 34 | 35 | maplit="0.1.6" 36 | -------------------------------------------------------------------------------- /framework/README.md: -------------------------------------------------------------------------------- 1 | # Framework 2 | 3 | > Framework is a detector for different frameworks in one projects 4 | 5 | License 6 | --- 7 | 8 | [![Phodal's Idea](http://brand.phodal.com/shields/idea-small.svg)](http://ideas.phodal.com/) 9 | 10 | @ 2020~2021 A [Phodal Huang](https://www.phodal.com)'s [Idea](http://github.com/phodal/ideas). This code is distributed under the MPL license. See `LICENSE` in this directory. 11 | -------------------------------------------------------------------------------- /framework/src/dependency/base_library.rs: -------------------------------------------------------------------------------- 1 | #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)] 2 | pub struct BaseLibrary { 3 | pub name: String, 4 | pub version: String, 5 | pub group: String, 6 | pub source: String, 7 | pub scope: String, 8 | } 9 | 10 | pub enum LibraryScope { 11 | Dev, 12 | Test, 13 | } 14 | 15 | impl BaseLibrary { 16 | pub fn is_dev(&self) -> bool { 17 | return self.scope == "Test"; 18 | } 19 | } 20 | 21 | #[cfg(test)] 22 | mod tests { 23 | use crate::dependency::base_library::BaseLibrary; 24 | 25 | #[test] 26 | fn should_be_dev_when_scope_dev() { 27 | let base_library = BaseLibrary { 28 | name: "some".to_string(), 29 | version: "0.1.1".to_string(), 30 | group: "".to_string(), 31 | source: "github.com".to_string(), 32 | scope: "Test".to_string(), 33 | }; 34 | 35 | assert!(base_library.is_dev()); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /framework/src/dependency/library_dependency.rs: -------------------------------------------------------------------------------- 1 | #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)] 2 | pub struct LibraryDependency {} 3 | -------------------------------------------------------------------------------- /framework/src/dependency/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod base_library; 2 | pub mod library_dependency; 3 | pub mod module_dependency; 4 | pub mod project_dependency; 5 | -------------------------------------------------------------------------------- /framework/src/dependency/module_dependency.rs: -------------------------------------------------------------------------------- 1 | #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)] 2 | pub struct ModuleDependency {} 3 | -------------------------------------------------------------------------------- /framework/src/dependency/project_dependency.rs: -------------------------------------------------------------------------------- 1 | #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)] 2 | pub struct ProjectDependency {} 3 | -------------------------------------------------------------------------------- /framework/src/facet/go/go_facet.rs: -------------------------------------------------------------------------------- 1 | #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)] 2 | pub struct GoFacet { 3 | pub has_mod: bool, 4 | } 5 | -------------------------------------------------------------------------------- /framework/src/facet/go/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod go_facet; 2 | -------------------------------------------------------------------------------- /framework/src/facet/java/content_root.rs: -------------------------------------------------------------------------------- 1 | #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)] 2 | pub struct ContentRoot { 3 | pub root_dirs: Vec, 4 | pub source_dirs: Vec, 5 | pub gen_source_dirs: Vec, 6 | pub resource_dirs: Vec, 7 | pub test_source_dirs: Vec, 8 | pub get_test_dirs: Vec, 9 | pub get_test_source_dirs: Vec, 10 | pub exclude_dirs: Vec, 11 | } 12 | 13 | impl Default for ContentRoot { 14 | fn default() -> Self { 15 | ContentRoot { 16 | root_dirs: vec![], 17 | source_dirs: vec![], 18 | gen_source_dirs: vec![], 19 | resource_dirs: vec![], 20 | test_source_dirs: vec![], 21 | get_test_dirs: vec![], 22 | get_test_source_dirs: vec![], 23 | exclude_dirs: vec![], 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /framework/src/facet/java/java_facet.rs: -------------------------------------------------------------------------------- 1 | use crate::facet::java::jvm_facet::JvmFacet; 2 | use crate::facet::Facet; 3 | use crate::lang::jvm; 4 | use crate::lang::jvm::{WORKSPACE_FRAMEWORK_GRADLE, WORKSPACE_FRAMEWORK_POM}; 5 | use std::collections::BTreeMap; 6 | 7 | #[derive(Serialize)] 8 | pub struct JavaFacet { 9 | pub jvm: JvmFacet, 10 | pub include_test: bool, 11 | } 12 | 13 | impl JavaFacet { 14 | pub fn new() -> JavaFacet { 15 | JavaFacet { 16 | jvm: Default::default(), 17 | include_test: false, 18 | } 19 | } 20 | } 21 | 22 | pub fn creator(tags: &BTreeMap<&str, bool>) -> Option> { 23 | if is_jvm_project(tags) { 24 | let facet = JavaFacet { 25 | jvm: JvmFacet { 26 | is_gradle: tags.contains_key(jvm::WORKSPACE_FRAMEWORK_GRADLE), 27 | is_maven: tags.contains_key(jvm::WORKSPACE_FRAMEWORK_POM), 28 | has_java: tags.contains_key(jvm::WORKSPACE_SOURCE_JAVA), 29 | has_groovy: tags.contains_key(jvm::WORKSPACE_SOURCE_GROOVY), 30 | has_kotlin: tags.contains_key(jvm::WORKSPACE_SOURCE_KOTLIN), 31 | has_scala: tags.contains_key(jvm::WORKSPACE_SOURCE_SCALA), 32 | }, 33 | include_test: tags.contains_key(jvm::WORKSPACE_HAS_TEST), 34 | }; 35 | return Some(Box::new(facet)); 36 | } 37 | None 38 | } 39 | 40 | fn is_jvm_project(tags: &BTreeMap<&str, bool>) -> bool { 41 | tags.contains_key(WORKSPACE_FRAMEWORK_GRADLE) || tags.contains_key(WORKSPACE_FRAMEWORK_POM) 42 | } 43 | -------------------------------------------------------------------------------- /framework/src/facet/java/java_module_data.rs: -------------------------------------------------------------------------------- 1 | use crate::facet::java::content_root::ContentRoot; 2 | 3 | #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)] 4 | pub struct JavaModuleData { 5 | pub module_name: String, 6 | pub content_roots: Vec, 7 | } 8 | 9 | impl Default for JavaModuleData { 10 | fn default() -> Self { 11 | JavaModuleData { 12 | module_name: "".to_string(), 13 | content_roots: vec![], 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /framework/src/facet/java/jvm_facet.rs: -------------------------------------------------------------------------------- 1 | #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)] 2 | pub struct JvmFacet { 3 | pub is_gradle: bool, 4 | pub is_maven: bool, 5 | 6 | pub has_java: bool, 7 | pub has_groovy: bool, 8 | pub has_kotlin: bool, 9 | pub has_scala: bool, 10 | } 11 | 12 | impl Default for JvmFacet { 13 | fn default() -> Self { 14 | JvmFacet { 15 | is_gradle: false, 16 | is_maven: false, 17 | has_java: false, 18 | has_groovy: false, 19 | has_kotlin: false, 20 | has_scala: false, 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /framework/src/facet/java/mod.rs: -------------------------------------------------------------------------------- 1 | pub use java_facet::JavaFacet; 2 | pub use java_module_data::JavaModuleData; 3 | 4 | pub mod content_root; 5 | pub mod java_facet; 6 | pub mod java_module_data; 7 | pub mod jvm_facet; 8 | -------------------------------------------------------------------------------- /framework/src/facet/javascript/javascript_facet.rs: -------------------------------------------------------------------------------- 1 | #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)] 2 | pub struct JavaScriptFacet { 3 | pub is_frontend: bool, 4 | pub is_angular: bool, 5 | pub is_react: bool, 6 | pub is_vue: bool, 7 | 8 | pub is_node: bool, 9 | pub is_browser: bool, 10 | pub is_ionic: bool, 11 | pub is_cordova: bool, 12 | pub is_bower: bool, 13 | pub is_hybrid: bool, 14 | 15 | pub is_typescript: bool, 16 | pub is_javascript: bool, 17 | 18 | pub has_grunt: bool, 19 | pub has_gulp: bool, 20 | } 21 | 22 | impl Default for JavaScriptFacet { 23 | fn default() -> Self { 24 | JavaScriptFacet { 25 | is_frontend: false, 26 | is_angular: false, 27 | is_react: false, 28 | is_vue: false, 29 | is_node: false, 30 | is_browser: false, 31 | is_ionic: false, 32 | is_cordova: false, 33 | is_bower: false, 34 | is_hybrid: false, 35 | is_typescript: false, 36 | is_javascript: false, 37 | has_grunt: false, 38 | has_gulp: false, 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /framework/src/facet/javascript/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod javascript_facet; 2 | 3 | pub use javascript_facet::JavaScriptFacet; 4 | -------------------------------------------------------------------------------- /framework/src/facet/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::facet::java::java_facet; 2 | pub use go::go_facet; 3 | pub use java::jvm_facet::JvmFacet; 4 | pub use java::JavaFacet; 5 | pub use java::JavaModuleData; 6 | pub use javascript::javascript_facet; 7 | pub use python::python_facet; 8 | pub use rust::rust_facet; 9 | use std::collections::BTreeMap; 10 | 11 | /// Java 12 | pub mod java; 13 | 14 | /// JavaScript 15 | pub mod javascript; 16 | 17 | /// Python 18 | pub mod python; 19 | 20 | /// golang 21 | pub mod go; 22 | 23 | /// rust 24 | pub mod rust; 25 | 26 | pub type Facet = dyn erased_serde::Serialize; 27 | pub type FacetCreator = fn(&BTreeMap<&str, bool>) -> Option>; 28 | 29 | pub struct FacetsBuilder { 30 | facets: Vec, 31 | } 32 | 33 | impl Default for FacetsBuilder { 34 | fn default() -> Self { 35 | FacetsBuilder { 36 | facets: vec![java_facet::creator], 37 | } 38 | } 39 | } 40 | 41 | impl FacetsBuilder { 42 | pub fn build(self, tags: &BTreeMap<&str, bool>) -> Vec> { 43 | let mut facets = Vec::new(); 44 | for creator in self.facets.iter() { 45 | match creator(tags) { 46 | Some(facet) => facets.push(facet), 47 | _ => continue, 48 | } 49 | } 50 | facets 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /framework/src/facet/python/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod python_facet; 2 | 3 | pub use python_facet::PythonFacet; 4 | -------------------------------------------------------------------------------- /framework/src/facet/python/python_facet.rs: -------------------------------------------------------------------------------- 1 | use regex::Regex; 2 | 3 | lazy_static! { 4 | static ref PYTHON_TEST: Regex = Regex::new(r"^test.*\.py").unwrap(); 5 | } 6 | 7 | #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)] 8 | pub struct PythonFacet { 9 | pub has_requirements: bool, 10 | pub include_test: bool, 11 | } 12 | 13 | impl PythonFacet { 14 | pub fn is_test(path: &str) -> bool { 15 | PYTHON_TEST.is_match(path) 16 | } 17 | } 18 | 19 | #[cfg(test)] 20 | mod tests { 21 | use super::*; 22 | 23 | #[test] 24 | fn should_ident_pytest_file() { 25 | assert_eq!(true, PythonFacet::is_test("test_hello.py")); 26 | assert_eq!(false, PythonFacet::is_test("hello.py")); 27 | assert_eq!(false, PythonFacet::is_test("testhellopy")); 28 | assert_eq!(false, PythonFacet::is_test("fatest.py")); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /framework/src/facet/rust/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod rust_facet; 2 | -------------------------------------------------------------------------------- /framework/src/facet/rust/rust_facet.rs: -------------------------------------------------------------------------------- 1 | #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)] 2 | pub struct RustFacet {} 3 | -------------------------------------------------------------------------------- /framework/src/lang/go.rs: -------------------------------------------------------------------------------- 1 | use crate::framework_detector::Frameworks; 2 | use walkdir::DirEntry; 3 | 4 | pub fn get_tag<'a>(entry: &DirEntry) -> Option<&'a str> { 5 | let file_name = entry.file_name().to_str().unwrap(); 6 | match file_name { 7 | "go.mod" | "main.go" => Some("workspace.go"), 8 | _ => None, 9 | } 10 | } 11 | 12 | pub fn framework_analysis(_entry: &DirEntry, _frameworks: &Frameworks) {} 13 | -------------------------------------------------------------------------------- /framework/src/lang/js.rs: -------------------------------------------------------------------------------- 1 | use crate::framework_detector::Frameworks; 2 | use walkdir::DirEntry; 3 | 4 | pub fn get_tag<'a>(entry: &DirEntry) -> Option<&'a str> { 5 | let file_name = entry.file_name().to_str().unwrap(); 6 | match file_name { 7 | "bower.json" | "bower_components" => Some("workspace.bower"), 8 | "package.json" | "node_modules" => Some("workspace.npm"), 9 | _ => None, 10 | } 11 | } 12 | 13 | pub fn framework_analysis(_entry: &DirEntry, _frameworks: &Frameworks) {} 14 | -------------------------------------------------------------------------------- /framework/src/lang/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::framework_detector::Frameworks; 2 | use std::collections::BTreeMap; 3 | use std::path::Path; 4 | use walkdir::{DirEntry, WalkDir}; 5 | 6 | pub mod go; 7 | pub mod js; 8 | pub mod jvm; 9 | pub mod rust; 10 | 11 | type TaggingAction<'a> = fn(dir: &DirEntry) -> Option<&'a str>; 12 | type FrameworkAnalysisAction = fn(dir: &DirEntry, frameworks: &Frameworks); 13 | 14 | struct LangDetector<'a> { 15 | tagging: TaggingAction<'a>, 16 | framework_analysis: FrameworkAnalysisAction, 17 | } 18 | 19 | pub struct LangDetectors<'a> { 20 | pub tags: BTreeMap<&'a str, bool>, 21 | pub frameworks: Frameworks, 22 | detectors: Vec>, 23 | } 24 | 25 | impl<'a> Default for LangDetectors<'a> { 26 | fn default() -> Self { 27 | LangDetectors { 28 | tags: BTreeMap::default(), 29 | detectors: vec![ 30 | LangDetector { 31 | tagging: jvm::get_tag, 32 | framework_analysis: jvm::framework_analysis, 33 | }, 34 | LangDetector { 35 | tagging: js::get_tag, 36 | framework_analysis: js::framework_analysis, 37 | }, 38 | LangDetector { 39 | tagging: go::get_tag, 40 | framework_analysis: go::framework_analysis, 41 | }, 42 | LangDetector { 43 | tagging: rust::get_tag, 44 | framework_analysis: rust::framework_analysis, 45 | }, 46 | ], 47 | frameworks: Frameworks::default(), 48 | } 49 | } 50 | } 51 | 52 | impl<'a> LangDetectors<'a> { 53 | pub fn detect>(&mut self, path: P) { 54 | traverse_project_directory(path, |dir_entry| { 55 | self.tagging(dir_entry); 56 | self.framework_analysis(dir_entry); 57 | }) 58 | } 59 | 60 | fn tagging(&mut self, dir_entry: &DirEntry) { 61 | for detector in self.detectors.iter() { 62 | match (detector.tagging)(dir_entry) { 63 | Some(tag) => { 64 | self.tags.insert(tag, true); 65 | } 66 | _ => continue, 67 | } 68 | } 69 | } 70 | 71 | fn framework_analysis(&mut self, dir_entry: &DirEntry) { 72 | for detector in self.detectors.iter() { 73 | (detector.framework_analysis)(dir_entry, &mut self.frameworks); 74 | } 75 | } 76 | } 77 | 78 | fn traverse_project_directory<'a, P: AsRef, F>(path: P, mut each_entry_callback: F) 79 | where 80 | F: FnMut(&DirEntry), 81 | { 82 | let walk_dir = WalkDir::new(path); 83 | for dir_entry in walk_dir.into_iter() { 84 | if dir_entry.is_err() { 85 | continue; 86 | } 87 | 88 | let entry = dir_entry.unwrap(); 89 | if entry.path().file_name().is_none() { 90 | println!("none file_name {:?}", entry.path()); 91 | continue; 92 | } 93 | 94 | (each_entry_callback)(&entry) 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /framework/src/lang/rust.rs: -------------------------------------------------------------------------------- 1 | use crate::framework_detector::Frameworks; 2 | use walkdir::DirEntry; 3 | 4 | pub fn get_tag<'a>(entry: &DirEntry) -> Option<&'a str> { 5 | let file_name = entry.file_name().to_str().unwrap(); 6 | match file_name { 7 | "Cargo.toml" => Some("workspace.cargo"), 8 | _ => None, 9 | } 10 | } 11 | 12 | pub fn framework_analysis(_entry: &DirEntry, _frameworks: &Frameworks) {} 13 | -------------------------------------------------------------------------------- /framework/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | 3 | #[macro_use] 4 | extern crate lazy_static; 5 | 6 | #[macro_use] 7 | extern crate maplit; 8 | 9 | #[macro_use] 10 | extern crate serde_derive; 11 | extern crate regex; 12 | extern crate serde; 13 | 14 | pub mod dependency; 15 | pub mod facet; 16 | pub mod framework_detector; 17 | pub mod lang; 18 | -------------------------------------------------------------------------------- /justfile: -------------------------------------------------------------------------------- 1 | tests: 2 | cargo test --all --exclude e2e 3 | 4 | e2e: 5 | cargo test --package e2e 6 | 7 | build: 8 | cargo build --all 9 | 10 | release: 11 | cargo build --verbose --release --all 12 | 13 | @bench: 14 | cargo bench 15 | 16 | @lint: 17 | rustup component add clippy 18 | rustup component add rustfmt 19 | cargo clippy -- -D warnings 20 | cargo clippy --tests 21 | cargo fmt -- --check 22 | 23 | @fix: 24 | cargo fmt --all 25 | 26 | # cargo install cargo-bloat 27 | @analysis: 28 | cargo bloat --release -n 50 29 | 30 | clean: 31 | cargo clean 32 | find . -type f -name "*.orig" -exec rm {} \; 33 | find . -type f -name "*.bk" -exec rm {} \; 34 | find . -type f -name ".*~" -exec rm {} \; 35 | 36 | changelog: 37 | conventional-changelog -p angular -i CHANGELOG.md -s 38 | -------------------------------------------------------------------------------- /locales/templates/concept/deploy-patterns.md: -------------------------------------------------------------------------------- 1 | | 部署或测试模式 | 零停机时间 | 实际生产流量测试 | 根据条件向用户发布 | 回滚时长 | 对硬件和云费用的影响 | 2 | | --- | --- | --- | --- | --- | --- | 3 | | **重新创建**
版本 1 已终止,版本 2 已发布。 | x | x | x | 速度快,但会由于停机时间而中断 | 无需额外设置 | 4 | | **滚动更新**
版本 2 逐步推出并取代版本 1。 | ✓ | x | x | 慢 | 可能需要对超额配置升级进行额外设置 | 5 | | **蓝绿**
版本 2 与版本 1 一同发布;流量在经过测试后会切换到版本 2 | ✓ | x | x | 即时 | 需要同时维护蓝色和绿色环境 | 6 | | **Canary 版**
版本 2 面向部分用户发布,随后全面发布。 | ✓ | ✓ | x | 快 | 无需额外设置 | 7 | | **A/B**
版本 2 在特定条件下面向一部分用户发布。 | ✓ | ✓ | ✓ | 快 | 无需额外设置 | 8 | | **影子**
版本 2 接收实际流量而不影响用户请求。 | ✓ | ✓ | x | 不适用 | 需要维护并行环境才能捕获和重放用户请求 | 9 | -------------------------------------------------------------------------------- /locales/templates/concept/sla.md: -------------------------------------------------------------------------------- 1 | | Availability % | Downtime per year | Downtime per quarter | Downtime per month | Downtime per week | Downtime per day | 2 | |----------------|----------------|----------------|----------------|----------------|----------------| 3 | | 90% ("one nine") | 36.53 days | 9.13 days | 73.05 hours | 16.80 hours | 2.40 hours | 4 | | 95% ("one and a half nines") | 18.26 days | 4.56 days | 36.53 hours | 8.40 hours | 1.20 hours | 5 | | 97% | 10.96 days | 2.74 days | 21.92 hours | 5.04 hours | 43.20 minutes | 6 | | 98% | 7.31 days | 43.86 hours | 14.61 hours | 3.36 hours | 28.80 minutes | 7 | | 99% ("two nines") | 3.65 days | 21.9 hours | 7.31 hours | 1.68 hours | 14.40 minutes | 8 | | 99.5% ("two and a half nines") | 1.83 days | 10.98 hours | 3.65 hours | 50.40 minutes | 7.20 minutes | 9 | | 99.8% | 17.53 hours | 4.38 hours | 87.66 minutes | 20.16 minutes | 2.88 minutes | 10 | | 99.9% ("three nines") | 8.77 hours | 2.19 hours | 43.83 minutes | 10.08 minutes | 1.44 minutes | 11 | | 99.95% ("three and a half nines") | 4.38 hours | 65.7 minutes | 21.92 minutes | 5.04 minutes | 43.20 seconds | 12 | | 99.99% ("four nines") | 52.60 minutes | 13.15 minutes | 4.38 minutes | 1.01 minutes | 8.64 seconds | 13 | | 99.995% ("four and a half nines") | 26.30 minutes | 6.57 minutes | 2.19 minutes | 30.24 seconds | 4.32 seconds | 14 | | 99.999% ("five nines") | 5.26 minutes | 1.31 minutes | 26.30 seconds | 6.05 seconds | 864.00 [milliseconds](https://en.wikipedia.org/wiki/Millisecond "Millisecond") | 15 | | 99.9999% ("six nines") | 31.56 seconds | 7.89 seconds | 2.63 seconds | 604.80 milliseconds | 86.40 milliseconds | 16 | | 99.99999% ("seven nines") | 3.16 seconds | 0.79 seconds | 262.98 milliseconds | 60.48 milliseconds | 8.64 milliseconds | 17 | | 99.999999% ("eight nines") | 315.58 milliseconds | 78.89 milliseconds | 26.30 milliseconds | 6.05 milliseconds | 864.00 [microseconds](https://en.wikipedia.org/wiki/Microsecond "Microsecond") | 18 | | 99.9999999% ("nine nines") | 31.56 milliseconds | 7.89 milliseconds | 2.63 milliseconds | 604.80 microseconds | 86.40 microseconds | 19 | -------------------------------------------------------------------------------- /locales/templates/local.hbs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inherd/coco/43ae3f3afd70705e0827d959e73efd89abc4a299/locales/templates/local.hbs -------------------------------------------------------------------------------- /locales/templates/modeling/practise.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inherd/coco/43ae3f3afd70705e0827d959e73efd89abc4a299/locales/templates/modeling/practise.md -------------------------------------------------------------------------------- /locales/templates/pipeline/practise.md: -------------------------------------------------------------------------------- 1 | from: https://www.jenkins.io/doc/book/pipeline/pipeline-best-practices/ 2 | 3 | stags: https://github.com/jenkinsci/pipeline-examples/blob/master/docs/BEST_PRACTICES.md 4 | 5 | # General 6 | 7 | ## Making sure to use Groovy code in Pipelines as glue 8 | 9 | ## Avoiding complex Groovy code in Pipelines 10 | 11 | ## Reducing repetition of similar Pipeline steps 12 | 13 | ## Avoiding calls to `Jenkins.getInstance` 14 | 15 | # Using shared libraries 16 | 17 | ## Do not override built-in Pipeline steps 18 | 19 | ## Avoiding large global variable declaration files 20 | 21 | ## Avoiding very large shared libraries 22 | 23 | -------------------------------------------------------------------------------- /locales/translate/en-US/suggest.ftl: -------------------------------------------------------------------------------- 1 | physical-design = Physical Design 2 | physical-design-description = Physical design focuses on the physical entities in the system and how they are interrelated. Logical design addresses only architectural issues; physical design addresses organizational issues. 3 | component-description = A component is the smallest unit of physical design. A component embodies a subset of the logical design that makes sense to exist as an independent, cohesive unit. 4 | package-description = A package is a collection of components organized as a physically cohesive unit. 5 | 6 | -------------------------------------------------------------------------------- /locales/translate/zh-CN/suggest.ftl: -------------------------------------------------------------------------------- 1 | physical-design = 物理设计 2 | physical-design-description = 物理设计集成于研究系统中的物理实体,及它们之间如何相互关联。逻辑设计只研究体系结构(架构)问题,物理设计研究组织问题。 3 | component-description = 一个组件就是物理设计的最小单位。一个组件包含一组逻辑设计的子集,该子集可以作为一个独立的、内聚的单位存在。 4 | package-description = 一个包就是被组织成一个物理内聚单位的组件集合。 5 | knowledge-transform = 知识转换成本依赖于项目复杂度(代码量、上手难度)、组织成员的变更数。 6 | learning-curve-desc = 项目的上手复杂度与开发人员的提交量成正比,即复杂的项目上手成本越高,短期的提交量越少。Coco 是根据最近 30 周的提交次数/代码修改行数,绘制出来的曲线。(适用于以团队为单位的软件开发)。 7 | members-lifecycle-desc = 软件团队的规模呈线性增长时,沟通成本介于线性增长和指数级增长之间。而离职人员数越多时,则会因为新成员不了解系统的设计,就可能增加出错的可能性。 8 | metrics-purpose = 1. 帮助跟踪和理解发生了什么。 2. 帮助人们沟通发生的事情。 3. 帮助人们关注那些他们真正需要改善的事情。 9 | -------------------------------------------------------------------------------- /plugins/coco_container/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "coco_container" 3 | version = "0.1.0" 4 | authors = ["Phodal Huang "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | dockerfile-parser = "0.7.1" 11 | 12 | [dependencies.core_model] 13 | path = "../../core_model" 14 | 15 | [lib] 16 | name = "coco_container" 17 | crate-type = ["cdylib"] 18 | -------------------------------------------------------------------------------- /plugins/coco_container/README.md: -------------------------------------------------------------------------------- 1 | # Container Analysis 2 | 3 | - [ ] Dockerfile 4 | - [ ] Kubernetes 5 | -------------------------------------------------------------------------------- /plugins/coco_container/src/coco_container_plugin.rs: -------------------------------------------------------------------------------- 1 | use dockerfile_parser::Dockerfile; 2 | use std::fs; 3 | use std::path::Path; 4 | 5 | pub fn analysis(path: &Path) -> Dockerfile { 6 | let err_msg = format!("cannot find file: {:?}", path.display()); 7 | let content = fs::read_to_string(path).expect(err_msg.as_str()); 8 | let dockerfile = Dockerfile::parse(content.as_str()).unwrap(); 9 | 10 | return dockerfile; 11 | } 12 | -------------------------------------------------------------------------------- /plugins/coco_container/src/lib.rs: -------------------------------------------------------------------------------- 1 | use core_model::CocoConfig; 2 | use core_model::PluginInterface; 3 | 4 | pub mod coco_container_plugin; 5 | 6 | pub struct CocoContainer {} 7 | 8 | impl PluginInterface for CocoContainer { 9 | fn name(&self) -> &'static str { 10 | "coco.swagger" 11 | } 12 | 13 | fn on_plugin_load(&self) {} 14 | 15 | fn on_plugin_unload(&self) {} 16 | 17 | fn execute(&self, config: CocoConfig) { 18 | println!("{:?}", config); 19 | } 20 | } 21 | 22 | impl Default for CocoContainer { 23 | fn default() -> Self { 24 | CocoContainer {} 25 | } 26 | } 27 | 28 | #[no_mangle] 29 | pub fn plugin() -> Box { 30 | Box::new(CocoContainer::default()) 31 | } 32 | 33 | #[cfg(test)] 34 | mod tests { 35 | use crate::coco_container_plugin::analysis; 36 | use std::path::PathBuf; 37 | 38 | pub fn dockerfile_dir() -> PathBuf { 39 | let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")) 40 | .parent() 41 | .unwrap() 42 | .parent() 43 | .unwrap() 44 | .to_path_buf(); 45 | 46 | return root_dir.clone().join("_fixtures").join("dockerfile"); 47 | } 48 | 49 | #[test] 50 | pub fn should_parse_dockerfile() { 51 | let dockerfile = analysis(&dockerfile_dir().join("Go.Dockerfile")); 52 | 53 | for stage in dockerfile.iter_stages() { 54 | println!("stage #{}", stage.index); 55 | for ins in stage.instructions { 56 | println!(" {:?}", ins); 57 | } 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /plugins/coco_pipeline/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "coco_pipeline" 3 | version = "0.1.0" 4 | authors = ["Phodal Huang "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | jenkinsfile = "0.2.1" 11 | 12 | # grammar generator 13 | pest = "2.1.3" 14 | pest_derive = "2.1.0" 15 | 16 | # serialize 17 | serde = "1" 18 | serde_derive = "1" 19 | serde_json = "1" 20 | 21 | # Option parser with custom derive support 22 | gumdrop = "0.8" 23 | 24 | # gitignore 25 | # docs: https://github.com/BurntSushi/ripgrep/tree/master/crates/ignore 26 | ignore = "0.4" 27 | 28 | [dependencies.core_model] 29 | path = "../../core_model" 30 | 31 | [lib] 32 | name = "coco_pipeline" 33 | crate-type = ["cdylib"] 34 | -------------------------------------------------------------------------------- /plugins/coco_pipeline/README.md: -------------------------------------------------------------------------------- 1 | # Coco Pipeline Analyser 2 | 3 | https://docs.github.com/cn/actions/learn-github-actions/migrating-from-jenkins-to-github-actions 4 | 5 | ```schedule 6 | pipeline { 7 | agent any 8 | triggers { 9 | cron('H/15 * * * 1-5') 10 | } 11 | } 12 | ``` 13 | 14 | examples: 15 | 16 | ```groovy 17 | pipeline { 18 | agent none 19 | stages { 20 | stage('Run Tests') { 21 | matrix { 22 | axes { 23 | axis { 24 | name: 'PLATFORM' 25 | values: 'macos', 'linux' 26 | } 27 | } 28 | agent { label "${PLATFORM}" } 29 | stages { 30 | stage('test') { 31 | tools { nodejs "node-12" } 32 | steps { 33 | dir("scripts/myapp") { 34 | sh(script: "npm install -g bats") 35 | sh(script: "bats tests") 36 | } 37 | } 38 | } 39 | } 40 | } 41 | } 42 | } 43 | } 44 | ``` 45 | -------------------------------------------------------------------------------- /plugins/coco_pipeline/src/coco_pipeline.rs: -------------------------------------------------------------------------------- 1 | use jenkinsfile::Jenkinsfile; 2 | use serde::{Deserialize, Serialize}; 3 | 4 | #[derive(Serialize, Deserialize, Debug, Clone)] 5 | pub struct CocoPipeline { 6 | pub name: String, 7 | pub stages: Vec, 8 | } 9 | 10 | impl CocoPipeline { 11 | pub fn from(jenkinsfile: Jenkinsfile) -> CocoPipeline { 12 | let mut pipeline = CocoPipeline { 13 | name: jenkinsfile.name, 14 | stages: vec![], 15 | }; 16 | 17 | for main_stage in &jenkinsfile.stages { 18 | let mut stage = CocoPipelineStage::new(main_stage.name.clone()); 19 | stage.steps = main_stage.steps.clone(); 20 | for sub_stage in &main_stage.sub_stages { 21 | stage.sub_stages.push(CocoPipelineStage { 22 | name: sub_stage.name.clone(), 23 | steps: sub_stage.steps.clone(), 24 | is_parallel: false, 25 | sub_stages: vec![], 26 | }) 27 | } 28 | 29 | pipeline.stages.push(stage); 30 | } 31 | 32 | pipeline 33 | } 34 | } 35 | 36 | #[derive(Serialize, Deserialize, Debug, Clone)] 37 | pub struct CocoPipelineStage { 38 | pub name: String, 39 | pub steps: Vec, 40 | pub is_parallel: bool, 41 | pub sub_stages: Vec, 42 | } 43 | 44 | impl CocoPipelineStage { 45 | pub fn new(name: String) -> CocoPipelineStage { 46 | Self { 47 | name, 48 | steps: vec![], 49 | is_parallel: false, 50 | sub_stages: vec![], 51 | } 52 | } 53 | } 54 | 55 | impl Default for CocoPipelineStage { 56 | fn default() -> Self { 57 | Self { 58 | name: "".to_string(), 59 | steps: vec![], 60 | is_parallel: false, 61 | sub_stages: vec![], 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /plugins/coco_pipeline/src/coco_pipeline_plugin.rs: -------------------------------------------------------------------------------- 1 | use crate::coco_pipeline::CocoPipeline; 2 | use core_model::url_format::uri_to_path; 3 | use core_model::{url_format, CocoConfig, Settings}; 4 | use ignore::Walk; 5 | use jenkinsfile::Jenkinsfile; 6 | use std::fs; 7 | use std::path::PathBuf; 8 | 9 | pub fn execute(config: CocoConfig) { 10 | for repo in &config.repos { 11 | let url_str = repo.url.as_str(); 12 | let origin_files = lookup_jenkinsfile(url_str); 13 | let mut results = vec![]; 14 | 15 | for path in origin_files { 16 | let contents = fs::read_to_string(path).expect("Something went wrong reading the file"); 17 | if let Some(jenkinsfile) = Jenkinsfile::from_str(contents.as_str()) { 18 | results.push(CocoPipeline::from(jenkinsfile)); 19 | } 20 | } 21 | 22 | let result = serde_json::to_string_pretty(&results).unwrap(); 23 | write_to_json_file(url_str, &result); 24 | } 25 | } 26 | 27 | fn write_to_json_file(url_str: &str, result: &String) { 28 | let file_name = url_format::json_filename(url_str); 29 | let output_file = Settings::pipeline().join(file_name); 30 | fs::write(output_file, result).expect("cannot write file"); 31 | } 32 | 33 | fn lookup_jenkinsfile(url_str: &str) -> Vec { 34 | let path = uri_to_path(url_str); 35 | let mut pipeline_files = vec![]; 36 | for result in Walk::new(path) { 37 | if let Ok(entry) = result { 38 | if !entry.file_type().unwrap().is_file() { 39 | continue; 40 | } 41 | 42 | if entry.file_name().to_str().unwrap() == "Jenkinsfile" { 43 | pipeline_files.push(entry.into_path()); 44 | } 45 | } 46 | } 47 | 48 | pipeline_files 49 | } 50 | -------------------------------------------------------------------------------- /plugins/coco_pipeline/src/github_action.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | #[derive(Serialize, Deserialize, Debug, Clone)] 4 | pub struct GitHubAction { 5 | pub builds: Vec, 6 | } 7 | 8 | #[derive(Serialize, Deserialize, Debug, Clone)] 9 | pub struct GithubBuild { 10 | pub name: String, 11 | pub jobs: Vec, 12 | } 13 | 14 | #[derive(Serialize, Deserialize, Debug, Clone)] 15 | pub struct GitHubJob { 16 | pub name: String, 17 | } 18 | -------------------------------------------------------------------------------- /plugins/coco_pipeline/src/jenkinsfile.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | #[derive(Serialize, Deserialize, Debug, Clone)] 4 | pub struct JenkinsFile { 5 | pub name: String, 6 | pub stages: Vec, 7 | pub post: Vec, 8 | } 9 | 10 | #[derive(Serialize, Deserialize, Debug, Clone)] 11 | pub struct JenkinsStage { 12 | pub name: String, 13 | pub jobs: Vec, 14 | } 15 | 16 | #[derive(Serialize, Deserialize, Debug, Clone)] 17 | pub struct JenkinsJob { 18 | pub name: String, 19 | pub job: String, 20 | } 21 | 22 | #[derive(Serialize, Deserialize, Debug, Clone)] 23 | pub struct PostConfig { 24 | pub key: String, 25 | pub value: Vec, 26 | } 27 | 28 | #[cfg(test)] 29 | mod tests { 30 | use jenkinsfile::Jenkinsfile; 31 | 32 | #[test] 33 | pub fn should_parse_hello_world() { 34 | let code = r#"pipeline { 35 | agent { docker 'maven:3.3.3' } 36 | stages { 37 | stage('build') { 38 | steps { 39 | sh 'mvn --version' 40 | } 41 | } 42 | } 43 | } 44 | "#; 45 | let jenkinsfile = Jenkinsfile::from_str(code).unwrap(); 46 | 47 | for stage in jenkinsfile.stages { 48 | println!("stage # {}", stage.name); 49 | for ins in stage.steps { 50 | println!("steps # {}", ins); 51 | } 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /plugins/coco_pipeline/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod coco_pipeline; 2 | pub mod coco_pipeline_plugin; 3 | pub mod github_action; 4 | pub mod jenkinsfile; 5 | 6 | use core_model::CocoConfig; 7 | use core_model::PluginInterface; 8 | 9 | pub struct CocoPipeline {} 10 | 11 | impl PluginInterface for CocoPipeline { 12 | fn name(&self) -> &'static str { 13 | "coco.pipeline" 14 | } 15 | 16 | fn on_plugin_load(&self) {} 17 | 18 | fn on_plugin_unload(&self) {} 19 | 20 | fn execute(&self, config: CocoConfig) { 21 | coco_pipeline_plugin::execute(config); 22 | } 23 | } 24 | 25 | impl Default for CocoPipeline { 26 | fn default() -> Self { 27 | CocoPipeline {} 28 | } 29 | } 30 | 31 | #[no_mangle] 32 | pub fn plugin() -> Box { 33 | Box::new(CocoPipeline::default()) 34 | } 35 | 36 | #[cfg(test)] 37 | mod tests { 38 | use crate::coco_pipeline::CocoPipeline; 39 | use crate::coco_pipeline_plugin::execute; 40 | use core_model::{CocoConfig, RepoConfig}; 41 | use std::fs::File; 42 | use std::io::Read; 43 | use std::path::PathBuf; 44 | 45 | pub fn fixtures_dir() -> PathBuf { 46 | let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")) 47 | .parent() 48 | .unwrap() 49 | .parent() 50 | .unwrap() 51 | .to_path_buf(); 52 | let ctags_dir = root_dir 53 | .clone() 54 | .join("_fixtures") 55 | .join("pipeline") 56 | .join("jenkinsfile"); 57 | 58 | return ctags_dir; 59 | } 60 | 61 | #[test] 62 | fn should_run_pipeline_analysis() { 63 | let mut repos = vec![]; 64 | repos.push(RepoConfig { 65 | url: format!("{}", fixtures_dir().display()), 66 | languages: None, 67 | }); 68 | let mut config = CocoConfig::default(); 69 | config.repos = repos; 70 | 71 | execute(config); 72 | 73 | let base_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")) 74 | .join(".coco") 75 | .join("reporter") 76 | .join("pipeline"); 77 | let output_dir = base_dir.join("jenkinsfile.json"); 78 | 79 | let mut file = File::open(output_dir).unwrap(); 80 | let mut code = String::new(); 81 | file.read_to_string(&mut code).unwrap(); 82 | let pipelines: Vec = serde_json::from_str(&code).unwrap(); 83 | 84 | assert_eq!(1, pipelines.len()); 85 | assert_eq!(5, pipelines[0].stages.len()); 86 | // assert_eq!(2, pipelines[0].stages[0].sub_stages.len()); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /plugins/coco_struct/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "coco_struct" 3 | version = "0.1.0" 4 | authors = ["Phodal Huang "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | nix = "0.19" 11 | tempfile = "3" 12 | failure = "0.1" 13 | 14 | # serialize 15 | serde = "1" 16 | serde_derive = "1" 17 | serde_json = "1" 18 | 19 | # command args to struct 20 | structopt = "0.3" 21 | structopt-toml = "0.4" 22 | 23 | lazy_static = "1.4.0" 24 | 25 | regex = "1" 26 | 27 | # gitignore 28 | # docs: https://github.com/BurntSushi/ripgrep/tree/master/crates/ignore 29 | ignore = "0.4" 30 | 31 | [dependencies.core_model] 32 | path = "../../core_model" 33 | 34 | [lib] 35 | name = "coco_struct" 36 | crate-type = ["cdylib"] 37 | -------------------------------------------------------------------------------- /plugins/coco_struct/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | cmd_ctags.rs,ctags_opt.rs,ctags_parser.rs based on [https://github.com/dalance/ptags](https://github.com/dalance/ptags) 4 | 5 | Copyright (c) 2018 dalance 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | -------------------------------------------------------------------------------- /plugins/coco_struct/README.md: -------------------------------------------------------------------------------- 1 | # Struct Analysis 2 | 3 | based on CTags 4 | 5 | `cmd_ctags` based on [https://github.com/dalance/ptags](https://github.com/dalance/ptags) 6 | 7 | ## contribute new language 8 | 9 | 1. run analysis command 10 | 11 | ``` 12 | ctags --fields=+latinK -R code_path/datastore.go 13 | ``` 14 | 15 | 2. copy generated `tags` to `coco/_fixtures/ctags` 16 | 17 | 3. write tests `in ctags_parser.rs` 18 | 19 | 20 | ## Todo 21 | 22 | Todo: 23 | 24 | - [x] fix tests 25 | - [x] output data 26 | - [x] create data struct dsl 27 | - [x] summary data struct 28 | - [ ] data struct uml 29 | - [ ] [https://github.com/projectstorm/react-diagrams](https://github.com/projectstorm/react-diagrams) a super simple, no-nonsense diagramming library written in react that just works 30 | - [ ] JointJs [https://github.com/clientIO/joint](https://github.com/clientIO/joint) 31 | -------------------------------------------------------------------------------- /plugins/coco_struct/src/ctags/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod ctags_cmd; 2 | pub mod ctags_opt; 3 | pub mod ctags_parser; 4 | -------------------------------------------------------------------------------- /plugins/coco_struct/src/plantuml/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod plantuml_render; 2 | -------------------------------------------------------------------------------- /plugins/coco_swagger/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "coco_swagger" 3 | version = "0.1.0" 4 | authors = ["Phodal Huang "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | openapi = { git = "https://github.com/softprops/openapi" } 11 | 12 | [dependencies.core_model] 13 | path = "../../core_model" 14 | 15 | [lib] 16 | name = "coco_swagger" 17 | crate-type = ["cdylib"] 18 | -------------------------------------------------------------------------------- /plugins/coco_swagger/README.md: -------------------------------------------------------------------------------- 1 | # Coco OpenAPI analysis 2 | 3 | ## RESTful API analsis ? 4 | 5 | 6 | -------------------------------------------------------------------------------- /plugins/coco_swagger/src/coco_swagger_plugin.rs: -------------------------------------------------------------------------------- 1 | extern crate openapi; 2 | 3 | use self::openapi::OpenApi; 4 | use std::path::Path; 5 | 6 | pub fn analysis(path: &Path) -> openapi::Result { 7 | openapi::from_path(path) 8 | } 9 | 10 | #[cfg(test)] 11 | mod tests { 12 | use super::openapi::OpenApi; 13 | use crate::coco_swagger_plugin::analysis; 14 | use std::path::PathBuf; 15 | 16 | pub fn swagger_dir() -> PathBuf { 17 | let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")) 18 | .parent() 19 | .unwrap() 20 | .parent() 21 | .unwrap() 22 | .to_path_buf(); 23 | let ctags_dir = root_dir 24 | .clone() 25 | .join("_fixtures") 26 | .join("swagger") 27 | .join("petstore.yaml"); 28 | 29 | return ctags_dir; 30 | } 31 | 32 | #[test] 33 | fn should_run_openapi_analysis() { 34 | let result = analysis(&*swagger_dir()).unwrap(); 35 | if let OpenApi::V3_0(spec) = result { 36 | let url = &spec.servers.unwrap()[0].url; 37 | assert_eq!("http://petstore.swagger.io/v1", url) 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /plugins/coco_swagger/src/lib.rs: -------------------------------------------------------------------------------- 1 | use core_model::CocoConfig; 2 | use core_model::PluginInterface; 3 | 4 | pub mod coco_swagger_plugin; 5 | 6 | pub struct CocoSwagger {} 7 | 8 | impl PluginInterface for CocoSwagger { 9 | fn name(&self) -> &'static str { 10 | "coco.swagger" 11 | } 12 | 13 | fn on_plugin_load(&self) {} 14 | 15 | fn on_plugin_unload(&self) {} 16 | 17 | fn execute(&self, config: CocoConfig) { 18 | println!("{:?}", config); 19 | } 20 | } 21 | 22 | impl Default for CocoSwagger { 23 | fn default() -> Self { 24 | CocoSwagger {} 25 | } 26 | } 27 | 28 | #[no_mangle] 29 | pub fn plugin() -> Box { 30 | Box::new(CocoSwagger::default()) 31 | } 32 | -------------------------------------------------------------------------------- /psa/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "psa" 3 | version = "0.1.1" 4 | authors = ["ynfeng ", "Phodal Huang", "Inherd Group "] 5 | edition = "2018" 6 | license = "MIT" 7 | readme = "README.md" 8 | repository = "https://github.com/phodal/coco" 9 | documentation = "https://github.com/phodal/coco" 10 | homepage = "https://github.com/phodal/coco" 11 | description = """ 12 | PSA(Project structure analysis) is a analyzer for analysis project struct. 13 | """ 14 | categories = ["text-processing", "command-line-interface", "development-tools"] 15 | exclude = [ 16 | "benchmark/*", 17 | "fixtures/*", 18 | ".github/*", 19 | ".gitattributes", 20 | ".adr.json", 21 | ] 22 | 23 | [dependencies] 24 | serde = { version = "1.0", features = ["derive"] } 25 | serde_derive = "1.0.115" 26 | serde_json = "1.0" 27 | erased-serde = "0.3" 28 | 29 | lazy_static = "1.2" 30 | 31 | walkdir = "2" 32 | 33 | regex = "1" 34 | 35 | pathdiff = "0.2.0" 36 | 37 | sxd-xpath= "0.4.2" 38 | 39 | sxd-document= "0.3.2" -------------------------------------------------------------------------------- /psa/README.md: -------------------------------------------------------------------------------- 1 | # psa 2 | 3 | > PSA(Project structure analysis) is a analyzer for analysis project struct. 4 | 5 | LICENSE 6 | --- 7 | 8 | @ 2020~2021 This code is distributed under the MIT license. See `LICENSE` in this directory. 9 | -------------------------------------------------------------------------------- /psa/src/dependency_analyzer.rs: -------------------------------------------------------------------------------- 1 | use crate::files::list_file_names; 2 | use crate::Dependency; 3 | use std::path::Path; 4 | 5 | pub trait DependencyAnalyzer { 6 | fn analysis(&self, module_path: &str) -> Vec { 7 | let build_file = self.get_build_file(module_path); 8 | match build_file { 9 | Some(build_file) => self.analysis_dependencies(module_path, build_file.as_str()), 10 | _ => vec![], 11 | } 12 | } 13 | 14 | fn get_build_file(&self, module_path: &str) -> Option { 15 | let file_names = list_file_names(Path::new(module_path)); 16 | file_names 17 | .iter() 18 | .find(|file| self.is_build_file(file.as_str())) 19 | .map(|file| file.to_string()) 20 | } 21 | 22 | fn is_build_file(&self, file: &str) -> bool; 23 | 24 | fn analysis_dependencies(&self, module_path: &str, build_file: &str) -> Vec; 25 | } 26 | -------------------------------------------------------------------------------- /psa/src/files.rs: -------------------------------------------------------------------------------- 1 | use pathdiff::diff_paths; 2 | use std::collections::HashSet; 3 | use std::path::Path; 4 | use std::path::PathBuf; 5 | use walkdir::WalkDir; 6 | 7 | pub fn list_file_names>(path: P) -> Vec { 8 | let mut files = Vec::new(); 9 | let walk_dir = WalkDir::new(path); 10 | for dir_entry in walk_dir.max_depth(1).into_iter() { 11 | if dir_entry.is_err() { 12 | panic!("{}", dir_entry.err().unwrap()); 13 | } 14 | 15 | let entry = dir_entry.unwrap(); 16 | if entry.metadata().unwrap().is_file() { 17 | files.push(entry.file_name().to_os_string().into_string().unwrap()); 18 | } 19 | } 20 | files 21 | } 22 | 23 | pub fn list_all>(path: P) -> HashSet { 24 | let mut dirs = HashSet::new(); 25 | let walk_dir = WalkDir::new(path); 26 | for dir_entry in walk_dir 27 | .min_depth(1) 28 | .sort_by(|a, b| a.file_name().cmp(b.file_name())) 29 | .into_iter() 30 | { 31 | if dir_entry.is_err() { 32 | panic!("{}", dir_entry.err().unwrap()); 33 | } 34 | 35 | dirs.insert(dir_entry.unwrap().path().display().to_string()); 36 | } 37 | dirs 38 | } 39 | 40 | pub fn list_sub_dirs>(path: P) -> Vec { 41 | let mut dirs = Vec::new(); 42 | let walk_dir = WalkDir::new(path); 43 | for dir_entry in walk_dir 44 | .min_depth(1) 45 | .max_depth(1) 46 | .sort_by(|a, b| a.file_name().cmp(b.file_name())) 47 | .into_iter() 48 | { 49 | if dir_entry.is_err() { 50 | panic!("{}", dir_entry.err().unwrap()); 51 | } 52 | 53 | let entry = dir_entry.unwrap(); 54 | if entry.metadata().unwrap().is_dir() { 55 | dirs.push(entry.path().display().to_string()) 56 | } 57 | } 58 | dirs 59 | } 60 | 61 | pub fn find_in_path(root_path: &str, file: Vec<&str>) -> Option { 62 | let all_files = list_all(root_path); 63 | let mut parent_path = PathBuf::from(root_path).to_path_buf(); 64 | for each_part in file.into_iter() { 65 | parent_path.push(each_part); 66 | } 67 | match all_files.contains(&parent_path.display().to_string()) { 68 | true => Some(parent_path.display().to_string()), 69 | _ => None, 70 | } 71 | } 72 | 73 | pub fn join_path(root_path: &str, file: Vec<&str>) -> String { 74 | let mut parent_path = PathBuf::from(root_path).to_path_buf(); 75 | for each_part in file.into_iter() { 76 | parent_path.push(each_part); 77 | } 78 | parent_path.display().to_string() 79 | } 80 | 81 | pub fn to_relative_path(base_path: &str, absolute_path: &str) -> String { 82 | diff_paths(absolute_path, base_path) 83 | .unwrap() 84 | .display() 85 | .to_string() 86 | } 87 | 88 | #[cfg(test)] 89 | mod tests { 90 | use crate::files::to_relative_path; 91 | 92 | #[test] 93 | fn should_convert_absolute_path_to_relative_path() { 94 | let relative_path = to_relative_path("/a/b/c", "/a/b/c/d/"); 95 | 96 | assert_eq!(relative_path, "d"); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /psa/src/jvm/maven_module.rs: -------------------------------------------------------------------------------- 1 | use std::path::Path; 2 | 3 | use crate::files::{find_in_path, list_file_names}; 4 | use crate::jvm::maven_dependency::MavenDependencyAnalyzer; 5 | use crate::Project; 6 | use crate::{DependencyAnalyzer, ModuleAnalyzer}; 7 | use std::collections::HashMap; 8 | 9 | pub struct MavenModuleAnalyzer {} 10 | 11 | impl ModuleAnalyzer for MavenModuleAnalyzer { 12 | fn has_build_file(&self, module_path: &str) -> bool { 13 | let file_names = list_file_names(module_path); 14 | 15 | for file_name in file_names.iter() { 16 | return match file_name.as_str() { 17 | "pom.xml" => true, 18 | _ => continue, 19 | }; 20 | } 21 | 22 | false 23 | } 24 | 25 | fn get_module_name(&self, project_path: &str) -> String { 26 | Path::new(project_path) 27 | .file_name() 28 | .unwrap() 29 | .to_os_string() 30 | .into_string() 31 | .unwrap() 32 | } 33 | 34 | fn is_related(&self, project: &Project) -> bool { 35 | project.project_type == "maven" 36 | } 37 | 38 | fn get_source_root(&self, module_path: &str) -> Option { 39 | find_in_path(module_path, vec!["src", "main", "java"]) 40 | } 41 | 42 | fn get_resource_root(&self, module_path: &str) -> Option { 43 | find_in_path(module_path, vec!["src", "main", "resources"]) 44 | } 45 | 46 | fn get_test_source_root(&self, module_path: &str) -> Option { 47 | find_in_path(module_path, vec!["src", "test", "java"]) 48 | } 49 | 50 | fn get_test_resource_root(&self, module_path: &str) -> Option { 51 | find_in_path(module_path, vec!["src", "test", "resources"]) 52 | } 53 | 54 | fn get_dependency_analyzer(&self) -> Box { 55 | Box::new(MavenDependencyAnalyzer { 56 | properties: HashMap::with_capacity(10), 57 | }) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /psa/src/jvm/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod maven_dependency; 2 | pub mod maven_module; 3 | pub mod psa_maven; 4 | -------------------------------------------------------------------------------- /psa/src/pas_content_root.rs: -------------------------------------------------------------------------------- 1 | #[derive(Serialize)] 2 | pub struct ContentRoot { 3 | pub source_root: Vec, 4 | pub resource_root: Vec, 5 | pub test_source_root: Vec, 6 | pub test_resource_root: Vec, 7 | } 8 | 9 | impl ContentRoot { 10 | pub fn add_source_root(&mut self, root_path: &str) { 11 | self.source_root.push(root_path.to_string()); 12 | } 13 | 14 | pub fn add_resource_root(&mut self, root_path: &str) { 15 | self.resource_root.push(root_path.to_string()); 16 | } 17 | 18 | pub fn add_test_source_root(&mut self, root_path: &str) { 19 | self.test_source_root.push(root_path.to_string()); 20 | } 21 | 22 | pub fn add_test_resource_root(&mut self, root_path: &str) { 23 | self.test_resource_root.push(root_path.to_string()); 24 | } 25 | } 26 | 27 | impl Default for ContentRoot { 28 | fn default() -> Self { 29 | ContentRoot { 30 | source_root: vec![], 31 | resource_root: vec![], 32 | test_source_root: vec![], 33 | test_resource_root: vec![], 34 | } 35 | } 36 | } 37 | 38 | #[cfg(test)] 39 | mod tests { 40 | use crate::ContentRoot; 41 | 42 | #[test] 43 | fn should_add_various_roots() { 44 | let mut content_root = ContentRoot::default(); 45 | 46 | content_root.add_source_root("src/main/java"); 47 | content_root.add_resource_root("src/main/resources"); 48 | content_root.add_test_source_root("src/test/java"); 49 | content_root.add_test_resource_root("src/test/resources"); 50 | 51 | assert_eq!(content_root.source_root, vec!["src/main/java".to_string()]); 52 | assert_eq!( 53 | content_root.resource_root, 54 | vec!["src/main/resources".to_string()] 55 | ); 56 | assert_eq!( 57 | content_root.test_source_root, 58 | vec!["src/test/java".to_string()] 59 | ); 60 | assert_eq!( 61 | content_root.test_resource_root, 62 | vec!["src/test/resources".to_string()] 63 | ); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /psa/src/project_structure_analyzer.rs: -------------------------------------------------------------------------------- 1 | use crate::psa_project::Project; 2 | use crate::{Module, ModuleAnalyzer}; 3 | 4 | pub trait ProjectStructureAnalyzer { 5 | fn analysis(&self, project_path: &str) -> Project { 6 | let project_name = self.get_project_name(project_path); 7 | let project_type = self.get_project_type(); 8 | 9 | let mut project = Project::new(project_name.as_str(), project_path, project_type.as_str()); 10 | 11 | if let Some(project_module) = self.analysis_project_module(&project) { 12 | project.set_project_module(project_module) 13 | } 14 | 15 | project 16 | } 17 | 18 | fn analysis_project_module(&self, project: &Project) -> Option { 19 | for module_analyzer in self.get_module_analyzers().iter() { 20 | return match module_analyzer.is_related(project) { 21 | true => module_analyzer.analysis(&project.absolute_path, &project.absolute_path), 22 | _ => continue, 23 | }; 24 | } 25 | None 26 | } 27 | 28 | fn get_project_name(&self, project_path: &str) -> String; 29 | fn get_project_type(&self) -> String; 30 | fn is_related(&self, project_path: &str) -> bool; 31 | fn get_module_analyzers(&self) -> Vec>; 32 | } 33 | -------------------------------------------------------------------------------- /psa/src/psa_dependency.rs: -------------------------------------------------------------------------------- 1 | #[derive(Serialize)] 2 | pub struct Dependency { 3 | pub name: String, 4 | pub group: String, 5 | pub version: String, 6 | pub scope: DependencyScope, 7 | } 8 | 9 | #[derive(Serialize, PartialEq, Debug)] 10 | pub enum DependencyScope { 11 | Test, 12 | Compile, 13 | } 14 | 15 | #[cfg(test)] 16 | mod tests { 17 | use crate::{Dependency, DependencyScope}; 18 | 19 | #[test] 20 | fn should_create_dependency() { 21 | let lib = Dependency { 22 | group: "org.springframework.boot".to_string(), 23 | name: "spring-boot-starter-web".to_string(), 24 | version: "1.0.0-RELEASE".to_string(), 25 | scope: DependencyScope::Compile, 26 | }; 27 | 28 | assert_eq!(lib.group, "org.springframework.boot".to_string()); 29 | assert_eq!(lib.name, "spring-boot-starter-web".to_string()); 30 | assert_eq!(lib.version, "1.0.0-RELEASE".to_string()); 31 | assert_eq!(lib.scope, DependencyScope::Compile); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /psa/src/psa_facet.rs: -------------------------------------------------------------------------------- 1 | #[derive(Serialize)] 2 | pub struct Facet { 3 | pub name: String, 4 | } 5 | 6 | #[cfg(test)] 7 | mod tests { 8 | use crate::Facet; 9 | 10 | #[test] 11 | fn should_create_facet() { 12 | let facet = Facet { 13 | name: "spring framework".to_string(), 14 | }; 15 | 16 | assert_eq!(facet.name, "spring framework"); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /psa/src/psa_project.rs: -------------------------------------------------------------------------------- 1 | use crate::psa_module::Module; 2 | 3 | #[derive(Serialize)] 4 | pub struct Project { 5 | pub name: String, 6 | pub absolute_path: String, 7 | pub project_module: Option, 8 | pub project_type: String, 9 | } 10 | 11 | impl Project { 12 | pub fn set_project_module(&mut self, module: Module) { 13 | self.project_module = Some(module); 14 | } 15 | 16 | pub fn new(name: &str, path: &str, project_type: &str) -> Self { 17 | Project { 18 | name: name.to_string(), 19 | absolute_path: path.to_string(), 20 | project_module: None, 21 | project_type: project_type.to_string(), 22 | } 23 | } 24 | } 25 | 26 | #[cfg(test)] 27 | mod tests { 28 | use crate::{Module, Project}; 29 | 30 | #[test] 31 | fn should_create_project() { 32 | let project = Project::new("foo", "test/path", "maven"); 33 | 34 | assert_eq!(project.name, "foo".to_string()); 35 | assert_eq!(project.absolute_path, "test/path".to_string()); 36 | assert_eq!(project.project_type, "maven".to_string()); 37 | assert_eq!(project.project_module.is_none(), true); 38 | } 39 | 40 | #[test] 41 | fn should_add_modules() { 42 | let mut project = Project::new("foo", "test/path", "maven"); 43 | 44 | project.set_project_module(Module::new("module1", "test/path/module1")); 45 | project.set_project_module(Module::new("module2", "test/path/module2")); 46 | 47 | assert_eq!(project.project_module.is_none(), false); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/app/analysis/architecture_analysis.rs: -------------------------------------------------------------------------------- 1 | use std::path::PathBuf; 2 | 3 | pub fn analysis(_path: PathBuf) -> String { 4 | return "{}".to_string(); 5 | } 6 | -------------------------------------------------------------------------------- /src/app/analysis/cloc_analysis.rs: -------------------------------------------------------------------------------- 1 | use crate::domain::cloc::{ClocDetail, ClocSummary}; 2 | use crate::infrastructure::cloc; 3 | use std::path::PathBuf; 4 | 5 | pub fn analysis(path: PathBuf) -> Vec { 6 | let mut languages = vec![]; 7 | for (lang_type, language) in cloc::by_dir(&path) { 8 | let mut details = vec![]; 9 | for report in language.reports { 10 | let strip_path = report.name.strip_prefix(&path).unwrap(); 11 | let file_name = strip_path 12 | .file_name() 13 | .unwrap() 14 | .to_str() 15 | .unwrap() 16 | .to_string(); 17 | 18 | details.push(ClocDetail { 19 | blanks: report.stats.blanks, 20 | code: report.stats.code, 21 | comments: report.stats.comments, 22 | file_name, 23 | path: strip_path.to_str().unwrap().to_string(), 24 | }); 25 | } 26 | 27 | languages.push(ClocSummary { 28 | language: lang_type.to_string(), 29 | blanks: language.blanks, 30 | code: language.code, 31 | comments: language.comments, 32 | reports: details, 33 | }) 34 | } 35 | 36 | return languages; 37 | } 38 | 39 | #[cfg(test)] 40 | mod test { 41 | use super::*; 42 | use std::path::{Path, PathBuf}; 43 | 44 | fn fixtures_dir() -> PathBuf { 45 | return PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("_fixtures"); 46 | } 47 | 48 | #[test] 49 | fn should_cloc_in_dir() { 50 | let buf = fixtures_dir().join("projects").join("java").join("hello"); 51 | let languages = analysis(buf); 52 | 53 | assert_eq!(2, languages.len()); 54 | assert_eq!("Java", languages[0].language); 55 | assert_eq!(1, languages[0].blanks); 56 | assert_eq!(6, languages[0].code); 57 | assert_eq!(1, languages[0].reports.len()); 58 | assert_eq!(1, languages[0].reports[0].blanks); 59 | assert_eq!(6, languages[0].reports[0].code); 60 | assert_eq!("HelloWorld.java", languages[0].reports[0].file_name); 61 | } 62 | 63 | #[test] 64 | fn should_cloc_in_dir_with_path_and_name() { 65 | let buf = fixtures_dir().join("projects").join("java").join("simple"); 66 | let languages = analysis(buf); 67 | 68 | assert_eq!("HelloWorld.java", languages[0].reports[0].file_name); 69 | 70 | let path = Path::new("app").join("HelloWorld.java"); 71 | 72 | assert_eq!(format!("{}", path.display()), languages[0].reports[0].path); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/app/analysis/framework_analysis.rs: -------------------------------------------------------------------------------- 1 | use std::path::PathBuf; 2 | 3 | use framework::framework_detector::FrameworkDetector; 4 | 5 | pub fn analysis(path: PathBuf) -> String { 6 | let mut detector = FrameworkDetector::default(); 7 | detector.run(path); 8 | 9 | return serde_json::to_string_pretty(&detector).unwrap(); 10 | } 11 | 12 | #[cfg(test)] 13 | mod test { 14 | use std::path::PathBuf; 15 | 16 | use super::*; 17 | 18 | #[test] 19 | fn should_return_json() { 20 | let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")).to_path_buf(); 21 | 22 | let test_project_dir = root_dir 23 | .clone() 24 | .join("_fixtures") 25 | .join("projects") 26 | .join("java") 27 | .join("simple"); 28 | 29 | let result = analysis(test_project_dir); 30 | assert_ne!("", result); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/app/analysis/git_analysis/branch_analysis.rs: -------------------------------------------------------------------------------- 1 | use super::FormatBranch; 2 | use crate::infrastructure::git::git_branch::GitBranch; 3 | use crate::infrastructure::git::GitRepository; 4 | 5 | pub fn analysis(url: &str, local_git: bool) -> Vec { 6 | let repo = GitRepository::open(url, local_git); 7 | 8 | let mut branches = vec![]; 9 | for br in GitBranch::list(repo) { 10 | branches.push(FormatBranch::from(br)); 11 | } 12 | 13 | return branches; 14 | } 15 | 16 | #[cfg(test)] 17 | mod test { 18 | use super::*; 19 | 20 | #[ignore] 21 | #[test] 22 | fn local_project_test() { 23 | let branches = analysis(".", false); 24 | assert!(branches.len() >= 2); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/app/analysis/git_analysis/commit_analysis.rs: -------------------------------------------------------------------------------- 1 | use crate::domain::git::CocoCommit; 2 | use crate::infrastructure::git::cmd_git::commit_message; 3 | use crate::infrastructure::git::git_log_parser::GitMessageParser; 4 | use core_model::coco_config::CocoCommitConfig; 5 | use core_model::url_format; 6 | use serde::{Deserialize, Serialize}; 7 | 8 | #[derive(Serialize, Deserialize, Debug, Clone)] 9 | pub struct ShortCommit { 10 | pub branch: String, 11 | pub story_id: String, 12 | pub commit_id: String, 13 | pub author: String, 14 | pub email: String, 15 | pub date: i64, 16 | pub message: String, 17 | pub parent_hashes: Vec, 18 | pub tree_hash: String, 19 | pub total_added: i32, 20 | pub total_deleted: i32, 21 | pub changed_file_count: i32, 22 | } 23 | 24 | impl ShortCommit { 25 | pub fn convert(commit: CocoCommit, commit_config: &Option) -> ShortCommit { 26 | let mut short_commit = Self { 27 | branch: commit.branch, 28 | story_id: "".to_string(), 29 | commit_id: commit.commit_id, 30 | author: commit.author, 31 | email: commit.email, 32 | date: commit.date, 33 | message: commit.message, 34 | parent_hashes: commit.parent_hashes, 35 | tree_hash: commit.tree_hash, 36 | total_added: commit.total_added, 37 | total_deleted: commit.total_deleted, 38 | changed_file_count: commit.changed_file_count, 39 | }; 40 | 41 | if let Some(config) = commit_config { 42 | if let Ok(hash) = CocoCommitConfig::verify_config(config) { 43 | if let Some(id) = hash.get("id") { 44 | short_commit.story_id = String::from(id) 45 | } 46 | } 47 | } 48 | 49 | short_commit 50 | } 51 | } 52 | 53 | pub fn analysis(url: &str, commit_config: Option) -> Vec { 54 | let local_path = url_format::uri_to_path(url); 55 | 56 | let messages = commit_message(Some(format!("{}", local_path.display()))); 57 | let vec = GitMessageParser::parse(messages.as_str()); 58 | 59 | let mut results = vec![]; 60 | for commit in vec { 61 | results.push(ShortCommit::convert(commit, &commit_config)) 62 | } 63 | 64 | return results; 65 | } 66 | -------------------------------------------------------------------------------- /src/app/analysis/git_analysis/file_analysis.rs: -------------------------------------------------------------------------------- 1 | use crate::infrastructure::git::git_file_history; 2 | use core_model::url_format; 3 | use git_scanner::flare::FlareTreeNode; 4 | 5 | pub fn analysis(url: &str, git_years: f64) -> FlareTreeNode { 6 | let local_path = url_format::uri_to_path(url); 7 | let tree_node = git_file_history::by_path(local_path, git_years); 8 | 9 | return tree_node; 10 | } 11 | -------------------------------------------------------------------------------- /src/app/analysis/git_analysis/format_branch.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | use crate::domain::git::CocoBranch; 4 | use crate::infrastructure::time_format::format_unix_time; 5 | 6 | #[derive(Serialize, Deserialize, Debug, Clone)] 7 | pub struct FormatBranch { 8 | pub name: String, 9 | pub first_commit_str: String, 10 | pub last_commit_str: String, 11 | pub first_commit_date: i64, 12 | pub last_commit_date: i64, 13 | pub commits: Vec, 14 | pub latest_changeset: String, 15 | } 16 | 17 | impl FormatBranch { 18 | pub fn from(br: CocoBranch) -> FormatBranch { 19 | FormatBranch { 20 | name: br.name, 21 | first_commit_str: format_unix_time(br.first_commit_date), 22 | last_commit_str: format_unix_time(br.last_commit_date), 23 | first_commit_date: br.first_commit_date, 24 | last_commit_date: br.last_commit_date, 25 | commits: br.commits, 26 | latest_changeset: br.latest_changeset, 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/app/analysis/git_analysis/mod.rs: -------------------------------------------------------------------------------- 1 | pub use branch_analysis::analysis; 2 | pub use format_branch::FormatBranch; 3 | 4 | pub mod branch_analysis; 5 | pub mod commit_analysis; 6 | pub mod file_analysis; 7 | pub mod format_branch; 8 | pub mod tag_analysis; 9 | -------------------------------------------------------------------------------- /src/app/analysis/git_analysis/tag_analysis.rs: -------------------------------------------------------------------------------- 1 | use crate::domain::git::coco_tag::CocoTag; 2 | use crate::infrastructure::git::cmd_git; 3 | use crate::infrastructure::git::git_tag_parser::GitTagParser; 4 | use core_model::url_format; 5 | 6 | pub fn analysis(url: &str) -> Vec { 7 | let local_path = url_format::uri_to_path(url); 8 | 9 | let messages = cmd_git::tags(Some(format!("{}", local_path.display()))); 10 | let results = GitTagParser::parse(messages.as_str()); 11 | 12 | return results; 13 | } 14 | 15 | #[cfg(test)] 16 | mod test { 17 | use super::*; 18 | 19 | #[ignore] 20 | #[test] 21 | fn local_project_test() { 22 | let tags = analysis("."); 23 | println!("{:?}", tags); 24 | assert!(tags.len() >= 2); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/app/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod analysis; 2 | pub mod plugin_helper; 3 | pub mod suggest; 4 | pub mod visual; 5 | 6 | pub use plugin_manager::PluginManager; 7 | pub mod plugin_manager; 8 | -------------------------------------------------------------------------------- /src/app/suggest/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod suggester; 2 | -------------------------------------------------------------------------------- /src/app/suggest/suggester.rs: -------------------------------------------------------------------------------- 1 | use crate::domain::cloc::ClocSummary; 2 | use crate::domain::suggest::ModelSuggest; 3 | use core_model::coco_struct::ClassInfo; 4 | use core_model::Settings; 5 | use std::fs; 6 | use std::path::PathBuf; 7 | 8 | pub struct Suggester; 9 | 10 | impl Suggester { 11 | pub fn run(project: String) { 12 | if let Ok(model) = Suggester::load_struct(project) { 13 | let suggest: ModelSuggest = ModelSuggest::new(model); 14 | suggest.analysis_all(); 15 | } 16 | } 17 | 18 | #[allow(dead_code)] 19 | fn load_cloc(project: String) -> Result, String> { 20 | let type_dir = Settings::cloc(); 21 | let contents = Suggester::read_content(project, type_dir)?; 22 | 23 | let model: Vec; 24 | match serde_json::from_str(contents.as_str()) { 25 | Ok(m) => model = m, 26 | Err(error) => { 27 | return Err(format!("{}", error)); 28 | } 29 | } 30 | return Ok(model); 31 | } 32 | 33 | fn load_struct(project: String) -> Result, String> { 34 | let type_dir = Settings::struct_dir(); 35 | let contents = Suggester::read_content(project, type_dir)?; 36 | 37 | let model: Vec; 38 | match serde_json::from_str(contents.as_str()) { 39 | Ok(m) => model = m, 40 | Err(error) => { 41 | return Err(format!("{}", error)); 42 | } 43 | } 44 | return Ok(model); 45 | } 46 | 47 | fn read_content(project: String, type_dir: PathBuf) -> Result { 48 | let file_name = format!("{}.json", project); 49 | let path = type_dir.join(file_name); 50 | let contents; 51 | match fs::read_to_string(path) { 52 | Ok(str) => contents = str, 53 | Err(error) => { 54 | return Err(format!("{}", error)); 55 | } 56 | } 57 | Ok(contents) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/app/visual/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod local_server; 2 | pub mod output_static; 3 | -------------------------------------------------------------------------------- /src/app/visual/output_static.rs: -------------------------------------------------------------------------------- 1 | use core_model::Settings; 2 | use rust_embed::RustEmbed; 3 | use std::fs; 4 | use std::path::Path; 5 | 6 | #[derive(RustEmbed)] 7 | #[folder = "web/"] 8 | struct Asset; 9 | 10 | pub fn run>(path: P, project: String) { 11 | for file in Asset::iter() { 12 | let file_name = format!("{}", file.as_ref()); 13 | let file_path = &path.as_ref().join(file.as_ref()); 14 | 15 | let content = Asset::get(&file_name).unwrap(); 16 | let _ = fs::create_dir_all(&file_path.parent().unwrap()); 17 | 18 | fs::write(file_path, content).expect("cannot write file"); 19 | } 20 | 21 | export_reporter(&path, project); 22 | } 23 | 24 | fn export_reporter>(path: &P, project: String) { 25 | let data_dir = path.as_ref().join("data"); 26 | let _ = fs::create_dir_all(&data_dir); 27 | 28 | // git 29 | let git = Settings::git().join(format!("{}.json", project)); 30 | let _ = fs::copy(git, &data_dir.join("git.json")); 31 | 32 | let commits = Settings::git().join(format!("{}-commits.json", project).as_str()); 33 | let _ = fs::copy(commits, &data_dir.join("git-commits.json")); 34 | 35 | let tags = Settings::git().join(format!("{}-tags.json", project)); 36 | let _ = fs::copy(tags, &data_dir.join("git-tags.json")); 37 | 38 | // file_history 39 | let file_history = Settings::git().join(format!("{}-file-history.json", project)); 40 | let _ = fs::copy(file_history, &data_dir.join("git-file-history.json")); 41 | 42 | // cloc 43 | let cloc = Settings::cloc().join(format!("{}.json", project)); 44 | let _ = fs::copy(cloc, &data_dir.join("cloc.json")); 45 | 46 | // struct analysis 47 | let structs = Settings::struct_dir().join(format!("{}.json", project)); 48 | let _ = fs::copy(structs, &data_dir.join("struct.json")); 49 | } 50 | -------------------------------------------------------------------------------- /src/bin/coco.rs: -------------------------------------------------------------------------------- 1 | use std::fs::OpenOptions; 2 | use std::{env, path::Path, process::exit}; 3 | 4 | use structopt::StructOpt; 5 | 6 | use coco::app::analysis; 7 | use coco::app::plugin_helper::PluginHelper; 8 | use coco::app::PluginManager; 9 | use coco::domain::{CocoCommand, CocoOpt}; 10 | use core_model::CocoConfig; 11 | 12 | const VERSION: &'static str = env!("CARGO_PKG_VERSION"); 13 | 14 | fn main() { 15 | let opt: CocoOpt = CocoOpt::from_args(); 16 | if let Some(sub_cmd) = opt.cmd { 17 | match sub_cmd { 18 | CocoCommand::Init => { 19 | create_config_file(); 20 | exit(0); 21 | } 22 | CocoCommand::Plugins => { 23 | let plugins_path = Path::new("coco_plugins"); 24 | PluginHelper::setup(&plugins_path, VERSION); 25 | exit(0); 26 | } 27 | } 28 | } 29 | 30 | let config_file = &opt.config_file; 31 | let config = CocoConfig::from_file(config_file); 32 | 33 | let is_debug = opt.debug; 34 | if is_debug { 35 | println!("found config file: {}", config_file); 36 | println!("{:?}", opt); 37 | } 38 | 39 | let analyst = analysis::Analyst::from(&config); 40 | analyst.analysis(opt); 41 | 42 | let plugin_manager = PluginManager::from(&config); 43 | plugin_manager.run_all(is_debug); 44 | } 45 | 46 | fn create_config_file() { 47 | println!("creating coco.yml"); 48 | match OpenOptions::new() 49 | .write(true) 50 | .create_new(true) 51 | .open("coco.yml") 52 | .map(|file| serde_yaml::to_writer(file, &CocoConfig::default()).unwrap()) 53 | { 54 | Ok(_) => println!("success created"), 55 | Err(e) => println!("coco.yml create failed: {}", e), 56 | } 57 | } 58 | 59 | #[cfg(test)] 60 | mod test { 61 | use std::env; 62 | 63 | use core_model::CocoConfig; 64 | 65 | #[test] 66 | fn should_set_default_config() { 67 | let config = CocoConfig::from_file(""); 68 | let current = env::current_dir().unwrap(); 69 | let url = current.into_os_string().to_str().unwrap().to_string(); 70 | 71 | assert_eq!(config.repos.len(), 1); 72 | assert_eq!(url, config.repos[0].url); 73 | assert!(config.plugins.is_none()) 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/bin/suggest.rs: -------------------------------------------------------------------------------- 1 | use structopt::StructOpt; 2 | 3 | use coco::app::suggest::suggester::Suggester; 4 | use coco::domain::SuggestOpt; 5 | use coco::infrastructure::file_scanner; 6 | use core_model::CocoConfig; 7 | 8 | fn main() { 9 | let opt: SuggestOpt = SuggestOpt::from_args(); 10 | 11 | let config_file = &opt.config_file; 12 | let _config = CocoConfig::from_file(config_file); 13 | 14 | let projects = file_scanner::lookup_projects(); 15 | for project in projects { 16 | Suggester::run(project); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/bin/visual.rs: -------------------------------------------------------------------------------- 1 | use dialoguer::{theme::ColorfulTheme, Select}; 2 | use webbrowser; 3 | 4 | use structopt::StructOpt; 5 | 6 | use coco::app::visual::{local_server, output_static}; 7 | use coco::domain::{SubVisualCommand, VisualOpt}; 8 | use coco::infrastructure::file_scanner; 9 | 10 | #[actix_web::main] 11 | async fn main() -> std::io::Result<()> { 12 | let opt: VisualOpt = VisualOpt::from_args(); 13 | 14 | if let Some(sub_cmd) = &opt.cmd { 15 | return match sub_cmd { 16 | SubVisualCommand::Export { output, name } => { 17 | let project = match name { 18 | Some(proj) => proj.to_string(), 19 | None => select_project_prompt(), 20 | }; 21 | 22 | start_export_reporter(output, project.clone()); 23 | Ok(()) 24 | } 25 | }; 26 | } 27 | 28 | let project = match opt.name { 29 | Some(proj) => proj.to_string(), 30 | None => select_project_prompt(), 31 | }; 32 | 33 | return start_local_server(project, opt.port.as_str()).await; 34 | } 35 | 36 | fn start_export_reporter(output: &String, project_name: String) { 37 | output_static::run(output, project_name); 38 | } 39 | 40 | async fn start_local_server(project: String, port: &str) -> std::io::Result<()> { 41 | let url = format!("http://127.0.0.1:{}", port); 42 | println!("start server: {}", url); 43 | 44 | open_url(&url); 45 | 46 | println!("project: {}", project); 47 | return local_server::start(port, project).await; 48 | } 49 | 50 | pub fn open_url(url: &str) { 51 | if let Err(err) = webbrowser::open(url) { 52 | println!("failure to open in browser: {}", err); 53 | } 54 | } 55 | 56 | pub fn select_project_prompt() -> String { 57 | let selections = file_scanner::lookup_projects(); 58 | if selections.len() == 0 { 59 | panic!("Please run coco first!"); 60 | } 61 | 62 | let selection = Select::with_theme(&ColorfulTheme::default()) 63 | .with_prompt("pick project") 64 | .default(0) 65 | .items(&selections[..]) 66 | .interact() 67 | .expect("1. Windows Users need to run with Windows Shell, such as PowerShell"); 68 | 69 | let project = selections[selection].clone(); 70 | project 71 | } 72 | -------------------------------------------------------------------------------- /src/coco_error.rs: -------------------------------------------------------------------------------- 1 | use reqwest; 2 | use std::{fmt, io}; 3 | use zip::result::ZipError; 4 | 5 | // Todo: optimize this mod 6 | 7 | /// Base Error 8 | pub struct CocoError { 9 | msg: String, 10 | } 11 | 12 | impl CocoError { 13 | pub fn new(msg: &str) -> Self { 14 | Self { 15 | msg: String::from(msg), 16 | } 17 | } 18 | } 19 | 20 | impl fmt::Display for CocoError { 21 | fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { 22 | write!(fmt, "CocoError: {}", self.msg) 23 | } 24 | } 25 | 26 | impl From for CocoError { 27 | fn from(err: io::Error) -> Self { 28 | Self { 29 | msg: format!("cause by: {}", err), 30 | } 31 | } 32 | } 33 | 34 | impl From for CocoError { 35 | fn from(_err: ZipError) -> Self { 36 | Self { 37 | msg: String::from("cause by: unzip error"), 38 | } 39 | } 40 | } 41 | 42 | impl From for CocoError { 43 | fn from(err: reqwest::Error) -> Self { 44 | Self { 45 | msg: format!("cause by: {}", err), 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/domain/architecture/mod.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)] 4 | pub struct Architecture { 5 | pub analysis: String, 6 | pub synthesis: String, 7 | pub evaluation: String, 8 | pub implementation: String, 9 | pub maintenance: String, 10 | pub evolution: String, 11 | } 12 | 13 | #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)] 14 | pub struct ArchitectureEvolution { 15 | pub fitness: ArchitectureFitness, 16 | } 17 | 18 | #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)] 19 | pub struct ArchitectureFitness { 20 | pub tests: String, 21 | } 22 | 23 | pub enum Practise { 24 | /// check in PR 25 | CodeReview, 26 | /// check pull request way? 27 | PullRequest, 28 | /// test with code change 29 | TDD, 30 | } 31 | 32 | pub enum LayerArchitecture { 33 | /// PresentationDomainDataLayering 34 | FlatMVC, 35 | /// Domain/PresentationDomainDataLayering 36 | NestedMVC, 37 | /// Domain-driven design 38 | DDD, 39 | /// Model with Behavior 40 | DomainObject, 41 | /// Model Object 42 | ModelObject, 43 | } 44 | -------------------------------------------------------------------------------- /src/domain/cli_opt/coco_opt.rs: -------------------------------------------------------------------------------- 1 | use structopt::StructOpt; 2 | 3 | #[derive(StructOpt, Debug, Clone)] 4 | #[structopt(name = "coco")] 5 | pub struct CocoOpt { 6 | /// Debug mode 7 | #[structopt(short, long, parse(try_from_str), default_value = "false")] 8 | pub debug: bool, 9 | 10 | /// Config file .yml 11 | #[structopt(short, long, default_value = "coco.yml")] 12 | pub config_file: String, 13 | 14 | /// With all commits 15 | #[structopt(long, parse(try_from_str), default_value = "true")] 16 | pub commits: bool, 17 | 18 | /// With all branches 19 | #[structopt(short, long, parse(try_from_str), default_value = "true")] 20 | pub branches: bool, 21 | 22 | /// Set git commits scan years, default 1, 23 | #[structopt(long, short = "y", parse(try_from_str), default_value = "1.0")] 24 | pub git_years: f64, 25 | 26 | /// Scan file change list from git & cloc 27 | #[structopt(long, short, short = "f", parse(try_from_str), default_value = "false")] 28 | pub file_history: bool, 29 | 30 | /// With all tags 31 | #[structopt(short, long, parse(try_from_str), default_value = "true")] 32 | pub tags: bool, 33 | 34 | /// Format commit message like, conventional commit, jira-format 35 | #[structopt(long, parse(try_from_str), default_value = "false")] 36 | pub format_commit: bool, 37 | 38 | #[structopt(subcommand)] 39 | pub cmd: Option, 40 | } 41 | 42 | #[derive(StructOpt, Debug, Clone)] 43 | pub enum CocoCommand { 44 | /// Create default coco.yml files 45 | Init, 46 | /// Download plugins from GitHub 47 | Plugins, 48 | } 49 | 50 | impl Default for CocoOpt { 51 | fn default() -> Self { 52 | CocoOpt { 53 | debug: false, 54 | config_file: "coco.yml".to_string(), 55 | commits: false, 56 | branches: false, 57 | git_years: 0.0, 58 | file_history: false, 59 | tags: false, 60 | format_commit: false, 61 | cmd: None, 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/domain/cli_opt/mod.rs: -------------------------------------------------------------------------------- 1 | pub use coco_opt::CocoCommand; 2 | pub use coco_opt::CocoOpt; 3 | 4 | pub use visual_opt::SubVisualCommand; 5 | pub use visual_opt::VisualOpt; 6 | 7 | pub use suggest_opt::SuggestOpt; 8 | 9 | pub mod coco_opt; 10 | pub mod suggest_opt; 11 | pub mod visual_opt; 12 | -------------------------------------------------------------------------------- /src/domain/cli_opt/suggest_opt.rs: -------------------------------------------------------------------------------- 1 | use structopt::StructOpt; 2 | 3 | #[derive(StructOpt, Debug, Clone)] 4 | #[structopt(name = "visual")] 5 | pub struct SuggestOpt { 6 | /// Debug mode 7 | #[structopt(short, long, parse(try_from_str), default_value = "false")] 8 | pub debug: bool, 9 | 10 | /// Config file .yml 11 | #[structopt(short, long, default_value = "coco.yml")] 12 | pub config_file: String, 13 | } 14 | -------------------------------------------------------------------------------- /src/domain/cli_opt/visual_opt.rs: -------------------------------------------------------------------------------- 1 | use structopt::StructOpt; 2 | 3 | #[derive(StructOpt, Debug, Clone)] 4 | #[structopt(name = "visual")] 5 | pub struct VisualOpt { 6 | /// Debug mode 7 | #[structopt(short, long, parse(try_from_str), default_value = "false")] 8 | pub debug: bool, 9 | 10 | /// http server port 11 | #[structopt(long, short, parse(try_from_str), default_value = "8000")] 12 | pub port: String, 13 | 14 | /// project name 15 | #[structopt(long, short, parse(try_from_str))] 16 | pub name: Option, 17 | 18 | #[structopt(subcommand)] 19 | pub cmd: Option, 20 | } 21 | 22 | #[derive(StructOpt, Debug, Clone)] 23 | pub enum SubVisualCommand { 24 | Export { 25 | /// output path 26 | #[structopt(long, short, parse(try_from_str), default_value = "coco_static")] 27 | output: String, 28 | 29 | #[structopt(long, short, parse(try_from_str))] 30 | name: Option, 31 | }, 32 | } 33 | -------------------------------------------------------------------------------- /src/domain/cloc/cloc_language.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | #[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] 4 | pub struct ClocSummary { 5 | pub language: String, 6 | pub blanks: usize, 7 | pub code: usize, 8 | pub comments: usize, 9 | pub reports: Vec, 10 | } 11 | 12 | impl Default for ClocSummary { 13 | fn default() -> Self { 14 | ClocSummary { 15 | language: "".to_string(), 16 | blanks: 0, 17 | code: 0, 18 | comments: 0, 19 | reports: vec![], 20 | } 21 | } 22 | } 23 | 24 | #[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] 25 | pub struct ClocDetail { 26 | /// Total blank lines 27 | pub blanks: usize, 28 | /// Total number of lines within the file. 29 | pub code: usize, 30 | /// Number of comments within the file. 31 | pub comments: usize, 32 | /// File name 33 | pub file_name: String, 34 | /// really path 35 | pub path: String, 36 | } 37 | 38 | impl Default for ClocDetail { 39 | fn default() -> Self { 40 | ClocDetail { 41 | blanks: 0, 42 | code: 0, 43 | comments: 0, 44 | file_name: "".to_string(), 45 | path: "".to_string(), 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/domain/cloc/mod.rs: -------------------------------------------------------------------------------- 1 | pub use cloc_language::ClocDetail; 2 | pub use cloc_language::ClocSummary; 3 | 4 | pub mod cloc_language; 5 | -------------------------------------------------------------------------------- /src/domain/code/code_relation.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)] 4 | pub struct CodeRelation { 5 | // thinking in to file name 6 | pub id: String, 7 | pub path: String, 8 | pub source: String, 9 | pub target: String, 10 | } 11 | -------------------------------------------------------------------------------- /src/domain/code/file_include.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)] 4 | pub struct FileInclude { 5 | pub includes: Vec, 6 | } 7 | 8 | #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)] 9 | pub struct CodeInclude { 10 | pub kind: String, 11 | pub name: String, 12 | } 13 | -------------------------------------------------------------------------------- /src/domain/code/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod code_relation; 2 | pub mod file_include; 3 | -------------------------------------------------------------------------------- /src/domain/framework/coco_framework.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)] 4 | pub struct CocoFramework { 5 | pub name: String, 6 | pub path: String, 7 | // for find the projects 8 | pub relative_path: String, 9 | // in some languages has different framework file 10 | // | languages | files | 11 | // |-------------|------------| 12 | // | Java | build.gradle, settings.gradle | 13 | pub framework_files: Vec, 14 | // in JVM projects, has different languages, such as Java, Groovy, Kotlin... 15 | pub languages: Vec, 16 | } 17 | -------------------------------------------------------------------------------- /src/domain/framework/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod coco_framework; 2 | -------------------------------------------------------------------------------- /src/domain/git/coco_branch.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | #[derive(Serialize, Deserialize, Debug, Clone)] 4 | pub struct CocoBranch { 5 | pub name: String, 6 | pub branch_type: String, 7 | pub first_commit_date: i64, 8 | pub last_commit_date: i64, 9 | pub duration: i64, 10 | pub commits_count: usize, 11 | pub commits: Vec, 12 | pub latest_changeset: String, 13 | } 14 | 15 | impl CocoBranch { 16 | pub fn new(name: &str) -> CocoBranch { 17 | CocoBranch { 18 | name: name.to_string(), 19 | branch_type: "".to_string(), 20 | first_commit_date: 0, 21 | last_commit_date: 0, 22 | duration: 0, 23 | commits_count: 0, 24 | commits: vec![], 25 | latest_changeset: "".to_string(), 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/domain/git/coco_commit.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | #[derive(Serialize, Deserialize, Debug, Clone)] 4 | pub struct CocoCommit { 5 | pub branch: String, 6 | pub commit_id: String, 7 | pub author: String, 8 | pub email: String, 9 | pub committer: String, 10 | pub date: i64, 11 | pub message: String, 12 | pub changes: Vec, 13 | pub parent_hashes: Vec, 14 | pub tree_hash: String, 15 | pub total_added: i32, 16 | pub total_deleted: i32, 17 | pub changed_file_count: i32, 18 | } 19 | 20 | impl Default for CocoCommit { 21 | fn default() -> Self { 22 | CocoCommit { 23 | branch: "".to_string(), 24 | commit_id: "".to_string(), 25 | author: "".to_string(), 26 | email: "".to_string(), 27 | committer: "".to_string(), 28 | date: 0, 29 | message: "".to_string(), 30 | changes: vec![], 31 | parent_hashes: vec![], 32 | tree_hash: "".to_string(), 33 | total_added: 0, 34 | total_deleted: 0, 35 | changed_file_count: 0, 36 | } 37 | } 38 | } 39 | 40 | #[derive(Serialize, Deserialize, Debug, Clone)] 41 | pub struct FileChange { 42 | pub added: i32, 43 | pub deleted: i32, 44 | pub file: String, 45 | pub mode: String, 46 | } 47 | -------------------------------------------------------------------------------- /src/domain/git/coco_commit_message.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | #[derive(Serialize, Deserialize, Debug, Clone)] 4 | pub struct ConventionalMessage { 5 | pub type_: String, 6 | pub scope: String, 7 | pub breaking: bool, 8 | pub subject: String, 9 | } 10 | 11 | impl Default for ConventionalMessage { 12 | fn default() -> Self { 13 | ConventionalMessage { 14 | type_: "".to_string(), 15 | scope: "".to_string(), 16 | breaking: false, 17 | subject: "".to_string(), 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/domain/git/coco_tag.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | #[derive(Serialize, Deserialize, Debug, Clone)] 4 | pub struct CocoTag { 5 | pub name: String, 6 | pub display_name: String, 7 | pub commit_id: String, 8 | pub date: i64, 9 | pub share_index: i64, 10 | } 11 | 12 | impl Default for CocoTag { 13 | fn default() -> Self { 14 | CocoTag { 15 | name: "".to_string(), 16 | display_name: "".to_string(), 17 | commit_id: "".to_string(), 18 | date: 0, 19 | share_index: 0, 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/domain/git/mod.rs: -------------------------------------------------------------------------------- 1 | pub use coco_branch::CocoBranch; 2 | pub use coco_commit::CocoCommit; 3 | 4 | pub mod coco_branch; 5 | pub mod coco_commit; 6 | pub mod coco_commit_message; 7 | pub mod coco_tag; 8 | 9 | #[allow(dead_code)] 10 | pub struct CocoCommitOpt { 11 | format_commits: bool, 12 | } 13 | -------------------------------------------------------------------------------- /src/domain/mod.rs: -------------------------------------------------------------------------------- 1 | pub use cli_opt::CocoCommand; 2 | pub use cli_opt::CocoOpt; 3 | pub use cli_opt::SubVisualCommand; 4 | pub use cli_opt::SuggestOpt; 5 | pub use cli_opt::VisualOpt; 6 | 7 | pub mod architecture; 8 | pub mod cli_opt; 9 | pub mod cloc; 10 | pub mod code; 11 | pub mod framework; 12 | pub mod git; 13 | pub mod suggest; 14 | -------------------------------------------------------------------------------- /src/domain/suggest/git_suggest.rs: -------------------------------------------------------------------------------- 1 | use crate::app::analysis::commit_analysis::ShortCommit; 2 | use crate::domain::git::coco_tag::CocoTag; 3 | 4 | #[allow(dead_code)] 5 | pub struct GitSuggest { 6 | tags: Vec, 7 | commits: Vec, 8 | } 9 | 10 | impl GitSuggest { 11 | /// count git tag interval for insight of release 12 | /// also same to publish interval 13 | /// 平均发布间隔 14 | pub fn git_tag_interval(&self) {} 15 | 16 | /// find multiple long branches working in process 17 | /// it will show the continuous delivery issue 18 | /// 最长分支 19 | pub fn long_branch_count(&self) {} 20 | 21 | /// show the data of weekend works' hours 22 | /// it will show the detail of hours 23 | /// 周末编码时间 24 | pub fn commits_in_weekend(&self) {} 25 | 26 | /// the time for max commits in days 27 | /// 最有效率时间 28 | pub fn most_efficiency_time(&self) {} 29 | 30 | /// show the average team members commit time 31 | /// frequently member's change means project's not stable for business project 32 | /// more members stay in a long time, will help project stable. 33 | /// unstable member's change need more Rules 34 | /// 平均成员编码时间区别 35 | pub fn average_members_coding_time_range(&self) {} 36 | 37 | /// the most active commits means the busy date 38 | /// 最活跃时间 39 | pub fn most_active_commits_date_by_month(&self) {} 40 | } 41 | -------------------------------------------------------------------------------- /src/domain/suggest/mod.rs: -------------------------------------------------------------------------------- 1 | pub use model_suggest::ModelSuggest; 2 | pub mod model_suggest; 3 | 4 | pub mod git_suggest; 5 | pub mod physical_suggest; 6 | pub mod pipeline_suggest; 7 | -------------------------------------------------------------------------------- /src/domain/suggest/model_suggest.rs: -------------------------------------------------------------------------------- 1 | use core_model::coco_struct::ClassInfo; 2 | 3 | #[allow(dead_code)] 4 | pub struct ModelSuggest { 5 | model: Vec, 6 | } 7 | 8 | impl ModelSuggest { 9 | pub fn new(model: Vec) -> ModelSuggest { 10 | ModelSuggest { model } 11 | } 12 | /// zh-CN: 过长参数 13 | /// en-US: Long Parameter List 14 | /// suggest: 15 | /// zh-CN: 引入参数对象 16 | /// en-US: Introduce Parameter Object 17 | pub fn find_long_parameter_list_method(&self) { 18 | // let max_parameters = 5; 19 | for info in &self.model { 20 | for method in &info.methods { 21 | if method.parameter_too_long() { 22 | println!("Parameter list too loong: {:?}", method); 23 | } 24 | } 25 | } 26 | } 27 | 28 | pub fn analysis_all(&self) { 29 | self.find_long_parameter_list_method(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/domain/suggest/physical_suggest.rs: -------------------------------------------------------------------------------- 1 | pub struct PhysicalSuggest {} 2 | 3 | impl PhysicalSuggest { 4 | /// cloc of directory 5 | /// 最复杂模块 6 | pub fn most_complex_modules() {} 7 | } 8 | -------------------------------------------------------------------------------- /src/domain/suggest/pipeline_suggest.rs: -------------------------------------------------------------------------------- 1 | pub struct PipelineSuggest; 2 | -------------------------------------------------------------------------------- /src/infrastructure/cloc/mod.rs: -------------------------------------------------------------------------------- 1 | use std::path::Path; 2 | 3 | use tokei::{Config, Languages, Sort}; 4 | 5 | pub fn by_dir>(path: P) -> Languages { 6 | let paths = &[path]; 7 | let excluded = &vec![]; 8 | 9 | let mut config = Config::default(); 10 | // todo: thinking in custom sort? 11 | config.sort = Some(Sort::Code); 12 | 13 | let mut languages = Languages::new(); 14 | 15 | languages.get_statistics(paths, excluded, &config); 16 | 17 | languages 18 | } 19 | 20 | #[cfg(test)] 21 | mod test { 22 | use std::path::PathBuf; 23 | 24 | use tokei::LanguageType; 25 | 26 | use crate::infrastructure::cloc::by_dir; 27 | 28 | fn fixtures_dir() -> PathBuf { 29 | return PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("_fixtures"); 30 | } 31 | 32 | #[test] 33 | fn should_cloc_in_dir() { 34 | let buf = fixtures_dir().join("projects").join("java").join("hello"); 35 | let languages = by_dir(buf); 36 | let java = &languages[&LanguageType::Java]; 37 | 38 | assert_eq!(1, java.blanks); 39 | } 40 | 41 | #[test] 42 | fn should_cloc_in_dir_ignore() { 43 | let buf = fixtures_dir().join("projects").join("java").join("simple"); 44 | let languages = by_dir(buf); 45 | let java = &languages[&LanguageType::Java]; 46 | 47 | assert_eq!(1, java.blanks); 48 | assert!(&languages.get(&LanguageType::JavaScript).is_none()); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/infrastructure/file_scanner/mod.rs: -------------------------------------------------------------------------------- 1 | use std::path::PathBuf; 2 | 3 | use walkdir::WalkDir; 4 | 5 | use core_model::Settings; 6 | 7 | pub fn search_git_projects(path: &PathBuf) -> Vec { 8 | return search_projects(path, ".git"); 9 | } 10 | 11 | pub fn lookup_projects() -> Vec { 12 | let mut projects = vec![]; 13 | // looking for one type 14 | let arch = Settings::architecture(); 15 | for entry in WalkDir::new(&arch).max_depth(1) { 16 | let entry = entry.unwrap(); 17 | let file_name = entry.file_name().to_os_string(); 18 | if file_name.to_str().unwrap().contains(".json") { 19 | let file_name = file_name.to_str().unwrap(); 20 | let project = file_name.replace(".json", ""); 21 | projects.push(project); 22 | } 23 | } 24 | 25 | return projects; 26 | } 27 | 28 | pub fn search_projects(path: &PathBuf, filter: &str) -> Vec { 29 | let mut results = vec![]; 30 | let mut has_first_level = false; 31 | for entry in WalkDir::new(&path).max_depth(1) { 32 | let entry = entry.unwrap(); 33 | if entry.path().ends_with(filter) { 34 | results.push(format!("{}", path.display())); 35 | has_first_level = true; 36 | } 37 | } 38 | 39 | if has_first_level { 40 | return results; 41 | } 42 | 43 | for entry in WalkDir::new(&path).max_depth(2) { 44 | let entry = entry.unwrap(); 45 | if entry.path().ends_with(filter) { 46 | let strip_path = entry.path().strip_prefix(&path).unwrap(); 47 | results.push(format!("{}", strip_path.display())); 48 | } 49 | } 50 | 51 | return results; 52 | } 53 | 54 | #[cfg(test)] 55 | mod test { 56 | use std::path::{Path, PathBuf}; 57 | 58 | use crate::infrastructure::file_scanner::search_projects; 59 | 60 | #[test] 61 | fn should_list_local_git() { 62 | let path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); 63 | let projects = search_projects(&path, ".git"); 64 | 65 | assert_eq!(1, projects.len()); 66 | assert_eq!(format!("{}", path.display()), projects[0]); 67 | } 68 | 69 | #[test] 70 | fn should_list_local_gittest() { 71 | let path = PathBuf::from(env!("CARGO_MANIFEST_DIR")) 72 | .join("_fixtures") 73 | .join("repos") 74 | .join("root"); 75 | let mut projects = search_projects(&path, ".gittest"); 76 | 77 | assert_eq!(2, projects.len()); 78 | projects.sort_by(|a, b| a.to_lowercase().cmp(&b.to_lowercase())); 79 | 80 | let proj1 = Path::new("app1").join(".gittest"); 81 | let proj2 = Path::new("app2").join(".gittest"); 82 | 83 | assert_eq!(format!("{}", proj1.display()), projects[0]); 84 | assert_eq!(format!("{}", proj2.display()), projects[1]); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/infrastructure/git/git_branch.rs: -------------------------------------------------------------------------------- 1 | use crate::domain::git::CocoBranch; 2 | use git2::{Commit, Repository, TreeWalkMode, TreeWalkResult}; 3 | 4 | pub struct GitBranch {} 5 | 6 | pub struct SimpleCommit { 7 | pub author: String, 8 | } 9 | 10 | impl GitBranch { 11 | pub fn list(repo: Repository) -> Vec { 12 | let branches = repo.branches(None).unwrap(); 13 | let mut coco_branches = vec![]; 14 | for x in branches { 15 | let branch = x.unwrap(); 16 | let br = &branch.0; 17 | let branch_type = format!("{:?}", &branch.1); 18 | 19 | // todo: add branch type support 20 | let branch_name = br.name().unwrap().unwrap(); 21 | let branch = GitBranch::calculate_branch(&repo, branch_name, &*branch_type); 22 | 23 | coco_branches.push(branch); 24 | } 25 | 26 | coco_branches 27 | } 28 | 29 | fn calculate_branch(repo: &Repository, branch_name: &str, branch_type: &str) -> CocoBranch { 30 | let mut branch = CocoBranch::new(branch_name); 31 | let oid = repo.revparse_single(branch_name).unwrap().id(); 32 | 33 | let mut walk = repo.revwalk().unwrap(); 34 | let _ = walk.push(oid); 35 | 36 | let mut commit_times = vec![]; 37 | let mut revwalk = walk.into_iter(); 38 | while let Some(oid_result) = revwalk.next() { 39 | if oid_result.is_err() { 40 | continue; 41 | } 42 | let oid = oid_result.unwrap(); 43 | let commit = repo.find_commit(oid).unwrap(); 44 | 45 | commit_times.push(commit.author().when().seconds()); 46 | branch.commits.push(oid.to_string()); 47 | } 48 | 49 | if commit_times.len() == 0 { 50 | panic!("not found commits"); 51 | } 52 | 53 | branch.latest_changeset = branch.commits.last().unwrap().to_string(); 54 | branch.last_commit_date = commit_times[0]; 55 | branch.commits_count = commit_times.len(); 56 | branch.first_commit_date = *commit_times.last().unwrap(); 57 | branch.branch_type = branch_type.to_string(); 58 | 59 | branch.duration = branch.last_commit_date - branch.first_commit_date; 60 | 61 | branch 62 | } 63 | 64 | pub fn get(name: &str, repo: Repository) -> Option { 65 | let filter: Vec = GitBranch::list(repo) 66 | .iter() 67 | .filter(|br| br.name == name) 68 | .cloned() 69 | .collect(); 70 | 71 | if filter.is_empty() { 72 | None 73 | } else { 74 | Some(filter[0].clone()) 75 | } 76 | } 77 | 78 | pub fn build_changes(commit: &Commit) { 79 | match commit.tree() { 80 | Ok(tree) => { 81 | tree.walk(TreeWalkMode::PreOrder, |_, entry| { 82 | println!("{:?}", entry.name().unwrap()); 83 | TreeWalkResult::Ok 84 | }) 85 | .unwrap(); 86 | } 87 | Err(_) => { 88 | println!() 89 | } 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/infrastructure/git/git_commit_message.rs: -------------------------------------------------------------------------------- 1 | use crate::domain::git::coco_commit_message::ConventionalMessage; 2 | use regex::Regex; 3 | 4 | lazy_static! { 5 | static ref CONVENTIONAL: Regex = Regex::new( 6 | r"(?x)(?Pbuild|chore|ci|docs|feat|fix|perf|refactor|revert|style|test) # type 7 | (?:\((?P[^()\r\n]*)\)|\()?(?P!)? # scope 8 | :\s? 9 | (?P.*)? # message 10 | " 11 | ) 12 | .unwrap(); 13 | } 14 | 15 | pub fn parse_builtin(message: &str) -> Option { 16 | let mut commit_message = ConventionalMessage::default(); 17 | if let Some(caps) = CONVENTIONAL.captures(message) { 18 | commit_message.type_ = (&caps["type"]).to_string(); 19 | commit_message.subject = (&caps["subject"]).to_string(); 20 | if let Some(_breaking) = caps.name("breaking") { 21 | commit_message.breaking = true; 22 | } 23 | if let Some(_scope) = caps.name("scope") { 24 | commit_message.scope = (&caps["scope"]).to_string(); 25 | } 26 | 27 | return Some(commit_message); 28 | } 29 | 30 | return None; 31 | } 32 | 33 | #[cfg(test)] 34 | mod test { 35 | use crate::infrastructure::git::git_commit_message::parse_builtin; 36 | 37 | #[test] 38 | fn should_parse_normal_conventional_message() { 39 | let msg = parse_builtin("build(visual): init project").unwrap(); 40 | assert_eq!("build", msg.type_); 41 | assert_eq!("init project", msg.subject); 42 | assert_eq!("visual", msg.scope); 43 | assert_eq!(false, msg.breaking); 44 | } 45 | 46 | #[test] 47 | fn should_parse_breaking_conventional_message() { 48 | let msg = parse_builtin("build(visual)!: init project").unwrap(); 49 | assert_eq!("build", msg.type_); 50 | assert_eq!("init project", msg.subject); 51 | assert_eq!("visual", msg.scope); 52 | assert_eq!(true, msg.breaking); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/infrastructure/git/git_repository.rs: -------------------------------------------------------------------------------- 1 | use git2::Repository; 2 | 3 | use crate::infrastructure::git::cmd_git; 4 | use core_model::url_format; 5 | use std::fs; 6 | 7 | pub struct GitRepository {} 8 | 9 | impl GitRepository { 10 | pub fn open(url: &str, local_git: bool) -> Repository { 11 | let local_path = url_format::uri_to_path(url); 12 | 13 | println!("target dir: {:?}", local_path.display()); 14 | if local_path.exists() { 15 | // todo: make update for repo 16 | let repo = match Repository::open(local_path) { 17 | Ok(repo) => repo, 18 | Err(e) => panic!("failed to open: {}", e), 19 | }; 20 | 21 | return repo; 22 | }; 23 | 24 | let _ = fs::create_dir_all(&local_path.parent().unwrap()); 25 | 26 | if local_git { 27 | let path_str = format!("{}", local_path.display()); 28 | cmd_git::clone(url, Some(path_str)); 29 | 30 | let repo = match Repository::open(local_path) { 31 | Ok(repo) => repo, 32 | Err(e) => panic!("failed to open: {}", e), 33 | }; 34 | 35 | return repo; 36 | } 37 | 38 | let repo = match Repository::clone(url, &local_path) { 39 | Ok(repo) => repo, 40 | Err(e) => { 41 | let exist_string = "exists and is not an empty directory"; 42 | 43 | if e.to_string().contains(exist_string) { 44 | return match Repository::open(local_path) { 45 | Ok(repo) => repo, 46 | Err(e) => panic!("failed to open: {}", e), 47 | }; 48 | } 49 | panic!("failed to clone: {}", e) 50 | } 51 | }; 52 | 53 | return repo; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/infrastructure/git/git_tag_parser.rs: -------------------------------------------------------------------------------- 1 | use crate::domain::git::coco_tag::CocoTag; 2 | use regex::Regex; 3 | 4 | lazy_static! { 5 | static ref TAG_INFO: Regex = Regex::new( 6 | r"(?x) 7 | (?P[\d|a-f]{5,12}) 8 | \s(?P\d{10}) 9 | \s\s\((?P.*)\)" 10 | ) 11 | .unwrap(); 12 | } 13 | 14 | pub struct GitTagParser { 15 | tags: Vec, 16 | } 17 | 18 | impl Default for GitTagParser { 19 | fn default() -> Self { 20 | GitTagParser { tags: vec![] } 21 | } 22 | } 23 | 24 | impl GitTagParser { 25 | pub fn parse(str: &str) -> Vec { 26 | let split = str.split("\n"); 27 | let mut parser = GitTagParser::default(); 28 | 29 | for line in split { 30 | parser.parse_log_by_line(line) 31 | } 32 | 33 | parser.tags 34 | } 35 | 36 | pub fn parse_log_by_line(&mut self, text: &str) { 37 | if let Some(captures) = TAG_INFO.captures(text) { 38 | let tags = (&captures["tags"]).split(","); 39 | let commit_id = &captures["commit_id"]; 40 | let date_str = &captures["date"]; 41 | let date = date_str.parse::().unwrap(); 42 | 43 | let mut share_index = 1; 44 | for tag in tags { 45 | if !tag.contains("tag:") { 46 | continue; 47 | } 48 | 49 | let tag = tag.split("tag: ").last().unwrap(); 50 | self.tags.push(CocoTag { 51 | name: tag.to_string(), 52 | display_name: tag.to_string(), 53 | commit_id: commit_id.to_string(), 54 | date, 55 | share_index, 56 | }); 57 | share_index = share_index + 1; 58 | } 59 | } 60 | } 61 | } 62 | 63 | #[cfg(test)] 64 | mod test { 65 | use crate::infrastructure::git::git_tag_parser::GitTagParser; 66 | 67 | #[test] 68 | pub fn should_parse_commit_id() { 69 | let input = "92fffa9b 1571521692 (tag: v0.21.0) 70 | 1fec6a3c 1570655888 71 | 71db1ab2 1541570931"; 72 | 73 | let tags = GitTagParser::parse(input); 74 | assert_eq!(1, tags.len()); 75 | assert_eq!("92fffa9b", &tags[0].commit_id); 76 | assert_eq!(1571521692, tags[0].date); 77 | assert_eq!("v0.21.0", tags[0].name); 78 | } 79 | 80 | #[test] 81 | pub fn should_not_parse_branch() { 82 | let input = "817b444 1611642635 (origin/add-license-1)"; 83 | let tags = GitTagParser::parse(input); 84 | 85 | assert_eq!(0, tags.len()); 86 | } 87 | 88 | #[test] 89 | pub fn should_parse_multiple_tags() { 90 | let input = "0f152d07 1582212561 (tag: v0.34.0, tag: std/0.34.0)"; 91 | let tags = GitTagParser::parse(input); 92 | 93 | assert_eq!(2, tags.len()); 94 | assert_eq!("std/0.34.0", tags[1].name); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/infrastructure/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod cloc; 2 | pub mod file_scanner; 3 | pub mod git; 4 | pub mod time_format; 5 | 6 | pub use translator::Translator; 7 | pub mod translator; 8 | -------------------------------------------------------------------------------- /src/infrastructure/time_format.rs: -------------------------------------------------------------------------------- 1 | extern crate chrono; 2 | 3 | use std::time::{Duration, UNIX_EPOCH}; 4 | 5 | use chrono::prelude::DateTime; 6 | use chrono::Utc; 7 | 8 | pub fn format_unix_time(i: i64) -> String { 9 | if i == 0 { 10 | return "".to_string(); 11 | } 12 | 13 | let d = UNIX_EPOCH + Duration::from_secs(i as u64); 14 | let datetime = DateTime::::from(d); 15 | let timestamp_str = datetime.format("%Y-%m-%d %H:%M:%S").to_string(); 16 | return timestamp_str; 17 | } 18 | 19 | #[cfg(test)] 20 | mod test { 21 | use crate::infrastructure::time_format::format_unix_time; 22 | 23 | #[test] 24 | fn format_commit_time() { 25 | let time = format_unix_time(1610509414); 26 | assert_eq!("2021-01-13 03:43:34", time); 27 | } 28 | 29 | #[test] 30 | fn format_commit_zero() { 31 | let time = format_unix_time(0); 32 | assert_eq!("", time); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/infrastructure/translator/mod.rs: -------------------------------------------------------------------------------- 1 | pub use translator::Translator; 2 | 3 | pub mod translator; 4 | -------------------------------------------------------------------------------- /src/infrastructure/translator/translator.rs: -------------------------------------------------------------------------------- 1 | use fluent::{FluentBundle, FluentResource}; 2 | use rust_embed::RustEmbed; 3 | use unic_langid::LanguageIdentifier; 4 | 5 | use std::borrow::Cow; 6 | use std::path::PathBuf; 7 | 8 | pub struct Translator { 9 | lang: String, 10 | content: String, 11 | } 12 | 13 | #[derive(RustEmbed)] 14 | #[folder = "locales/"] 15 | struct LocalesAsset; 16 | 17 | impl Translator { 18 | pub fn new(lang: &str) -> Translator { 19 | let path = PathBuf::from("translate").join(lang).join("suggest.ftl"); 20 | let path_str = format!("{}", path.display()); 21 | let file_content: Cow<'static, [u8]> = LocalesAsset::get(&path_str).unwrap(); 22 | let content = std::str::from_utf8(file_content.as_ref()).expect("cannot read file"); 23 | 24 | return Translator { 25 | lang: lang.to_string(), 26 | content: content.to_string(), 27 | }; 28 | } 29 | 30 | pub fn translate(self, content: &str) -> String { 31 | let res = FluentResource::try_new(self.content).expect("Failed to parse an FTL string."); 32 | 33 | let langid: LanguageIdentifier = self.lang.parse().expect("Parsing language failed."); 34 | let mut bundle = FluentBundle::new(vec![langid]); 35 | 36 | bundle 37 | .add_resource(&res) 38 | .expect("Failed to add FTL resources to the bundle."); 39 | 40 | let msg = bundle.get_message(content).expect("Message doesn't exist."); 41 | let mut errors = vec![]; 42 | let pattern = msg.value().expect("Message has no value."); 43 | let value = bundle.format_pattern(&pattern, None, &mut errors); 44 | 45 | return value.to_string(); 46 | } 47 | } 48 | 49 | #[cfg(test)] 50 | mod test { 51 | use crate::infrastructure::translator::Translator; 52 | 53 | #[test] 54 | pub fn should_translate_physical_design() { 55 | let translator = Translator::new("zh-CN"); 56 | let string = translator.translate("physical-design"); 57 | assert_eq!("物理设计", string); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate lazy_static; 3 | extern crate serde; 4 | 5 | extern crate pest; 6 | extern crate pest_derive; 7 | 8 | extern crate dlopen; 9 | #[macro_use] 10 | extern crate dlopen_derive; 11 | 12 | pub mod app; 13 | pub mod coco_error; 14 | pub mod domain; 15 | pub mod infrastructure; 16 | -------------------------------------------------------------------------------- /web/fake/.gitignore: -------------------------------------------------------------------------------- 1 | *.csv 2 | *.json 3 | -------------------------------------------------------------------------------- /web/fake/.gitkeeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inherd/coco/43ae3f3afd70705e0827d959e73efd89abc4a299/web/fake/.gitkeeep -------------------------------------------------------------------------------- /web/public/css/context-menu.css: -------------------------------------------------------------------------------- 1 | .context-menu { 2 | stroke: #00557d; 3 | fill: #ffffff; 4 | } 5 | 6 | .menu-entry { 7 | cursor: pointer; 8 | } 9 | 10 | .menu-entry text { 11 | font-size: 12px; 12 | stroke: #00557d; 13 | } 14 | -------------------------------------------------------------------------------- /web/public/css/layout.css: -------------------------------------------------------------------------------- 1 | /* hour-heatmap: layout */ 2 | .layout-grid2x2 { 3 | display: flex; 4 | flex-flow: row wrap; 5 | list-style: none; 6 | } 7 | 8 | .layout-grid2x2 > div { 9 | flex-basis: calc(50% - 20px); 10 | width: 50%; 11 | height: auto; 12 | } 13 | -------------------------------------------------------------------------------- /web/public/css/reset.css: -------------------------------------------------------------------------------- 1 | /* Box sizing rules */ 2 | *, 3 | *::before, 4 | *::after { 5 | box-sizing: border-box; 6 | } 7 | 8 | /* Remove default margin */ 9 | body, 10 | h1, 11 | h2, 12 | h3, 13 | h4, 14 | p, 15 | figure, 16 | blockquote, 17 | dl, 18 | dd { 19 | margin: 0; 20 | } 21 | 22 | /* Remove list styles on ul, ol elements with a list role, which suggests default styling will be removed */ 23 | ul[role="list"], 24 | ol[role="list"] { 25 | list-style: none; 26 | } 27 | 28 | /* Set core root defaults */ 29 | html:focus-within { 30 | scroll-behavior: smooth; 31 | } 32 | 33 | /* Set core body defaults */ 34 | body { 35 | min-height: 100vh; 36 | text-rendering: optimizeSpeed; 37 | line-height: 1.5; 38 | } 39 | 40 | /* A elements that don't have a class get default styles */ 41 | a:not([class]) { 42 | text-decoration-skip-ink: auto; 43 | } 44 | 45 | /* Make images easier to work with */ 46 | img, 47 | picture { 48 | max-width: 100%; 49 | display: block; 50 | } 51 | 52 | /* Inherit fonts for inputs and buttons */ 53 | input, 54 | button, 55 | textarea, 56 | select { 57 | font: inherit; 58 | } 59 | 60 | /* Remove all animations and transitions for people that prefer not to see them */ 61 | @media (prefers-reduced-motion: reduce) { 62 | html:focus-within { 63 | scroll-behavior: auto; 64 | } 65 | *, 66 | *::before, 67 | *::after { 68 | animation-duration: 0.01ms !important; 69 | animation-iteration-count: 1 !important; 70 | transition-duration: 0.01ms !important; 71 | scroll-behavior: auto !important; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /web/public/css/slide.css: -------------------------------------------------------------------------------- 1 | /* slider */ 2 | #play-button { 3 | position: relative; 4 | left: 50px; 5 | background: #f08080; 6 | border-radius: 3px; 7 | border: none; 8 | color: white; 9 | margin: 0; 10 | padding: 0 12px; 11 | width: 60px; 12 | cursor: pointer; 13 | height: 30px; 14 | } 15 | 16 | #play-button:hover { 17 | background-color: #696969; 18 | } 19 | 20 | .ticks { 21 | font-size: 10px; 22 | } 23 | 24 | .track, 25 | .track-inset, 26 | .track-overlay { 27 | stroke-linecap: round; 28 | } 29 | 30 | .track { 31 | stroke: #000; 32 | stroke-opacity: 0.3; 33 | stroke-width: 10px; 34 | } 35 | 36 | .track-inset { 37 | stroke: #dcdcdc; 38 | stroke-width: 8px; 39 | } 40 | 41 | .track-overlay { 42 | pointer-events: stroke; 43 | stroke-width: 50px; 44 | stroke: transparent; 45 | cursor: crosshair; 46 | } 47 | 48 | .handle { 49 | fill: #fff; 50 | stroke: #000; 51 | stroke-opacity: 0.5; 52 | stroke-width: 1.25px; 53 | } 54 | -------------------------------------------------------------------------------- /web/public/css/tooltip.css: -------------------------------------------------------------------------------- 1 | /* tooltip */ 2 | .tooltip { 3 | font-size: 14px; 4 | position: absolute; 5 | text-align: left; 6 | background: white; 7 | border-radius: 5px; 8 | box-shadow: 0 0 10px rgba(0,0,0,.25); 9 | line-height: 1.3; 10 | padding: 5px; 11 | width: auto; 12 | } 13 | -------------------------------------------------------------------------------- /web/public/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inherd/coco/43ae3f3afd70705e0827d959e73efd89abc4a299/web/public/images/favicon.ico -------------------------------------------------------------------------------- /web/public/js/graph-config.js: -------------------------------------------------------------------------------- 1 | let GraphConfig = { 2 | screen_width: window.innerWidth / 2, 3 | width: 1200, 4 | height: 900, 5 | } 6 | -------------------------------------------------------------------------------- /web/public/js/graph/plugins/struct-visual.js: -------------------------------------------------------------------------------- 1 | function visualizationStruct(data) { 2 | console.log(data); 3 | } 4 | -------------------------------------------------------------------------------- /web/public/js/plugins/seedrandom.min.js: -------------------------------------------------------------------------------- 1 | !function(a,b){function c(c,j,k){var n=[];j=1==j?{entropy:!0}:j||{};var s=g(f(j.entropy?[c,i(a)]:null==c?h():c,3),n),t=new d(n),u=function(){for(var a=t.g(m),b=p,c=0;a=r;)a/=2,b/=2,c>>>=1;return(a+c)/b};return u.int32=function(){return 0|t.g(4)},u.quick=function(){return t.g(4)/4294967296},u.double=u,g(i(t.S),a),(j.pass||k||function(a,c,d,f){return f&&(f.S&&e(f,t),a.state=function(){return e(t,{})}),d?(b[o]=a,c):a})(u,s,"global"in j?j.global:this==b,j.state)}function d(a){var b,c=a.length,d=this,e=0,f=d.i=d.j=0,g=d.S=[];for(c||(a=[c++]);e= 0) { 32 | let found = find({name: name.substring(0, i), children: []}); 33 | if (found.children) { 34 | found.children.push(data); 35 | } else { 36 | return data 37 | } 38 | data.name = name.substring(i + 1); 39 | data.value = value; 40 | } else { 41 | root = data; 42 | } 43 | return data; 44 | }); 45 | 46 | return root; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /web/public/js/support/dom-support.js: -------------------------------------------------------------------------------- 1 | let count = 0; 2 | 3 | function Id(id) { 4 | this.id = id; 5 | this.href = new URL(`#${id}`, location) + ""; 6 | } 7 | 8 | Id.prototype.toString = function () { 9 | return "url(" + this.href + ")"; 10 | }; 11 | 12 | let DOM = { 13 | uid: function (name) { 14 | return new Id("O-" + (name == null ? "" : name + "-") + ++count) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /web/public/js/support/menu-support.js: -------------------------------------------------------------------------------- 1 | let MenuSupport = { 2 | menuFactory: function (x, y, menuItems, data, svg, offsetOptions) { 3 | d3.select(".context-menu").remove(); 4 | 5 | // Draw the menu 6 | svg.append('g') 7 | .attr('class', "context-menu") 8 | .attr('transform', function (d) { 9 | if (!!offsetOptions && offsetOptions.width) { 10 | return 'translate(' + offsetOptions.width + ',' + offsetOptions.height + ')' 11 | } 12 | return 'translate(0, 10)'; 13 | }) 14 | .selectAll('tmp') 15 | .data(menuItems).enter() 16 | .append('g') 17 | .attr('class', "menu-entry") 18 | .style({'cursor': 'pointer'}); 19 | 20 | // Draw menu entries 21 | d3.selectAll(`.menu-entry`) 22 | .append('rect') 23 | .attr('x', x) 24 | .attr('y', (d, i) => { 25 | return y + (i * 30); 26 | }) 27 | .attr('rx', 2) 28 | .attr('width', 150) 29 | .attr('height', 30) 30 | .on('click', (event, d) => { 31 | d.action(data) 32 | }); 33 | 34 | d3.selectAll(`.menu-entry`) 35 | .append('text') 36 | .text((d) => { 37 | return d.title; 38 | }) 39 | .attr('x', x) 40 | .attr('y', (d, i) => { 41 | return y + (i * 30); 42 | }) 43 | .attr('dy', 20) 44 | .attr('dx', 45) 45 | .on('click', (event, d) => { 46 | d.action(data) 47 | }); 48 | 49 | // Other interactions 50 | d3.select('body') 51 | .on('click', () => { 52 | d3.select(".context-menu").remove(); 53 | }); 54 | }, 55 | createContextMenu: function (event, d, menuItems, svg, offsetOptions) { 56 | MenuSupport.menuFactory(event.layerX, event.layerY, menuItems, d, svg, offsetOptions); 57 | event.preventDefault(); 58 | }, 59 | copyText: function (text) { 60 | let ta = document.createElement("textarea"); 61 | ta.value = text; 62 | ta.style.position = 'absolute'; 63 | ta.style.left = "-999999999px"; 64 | document.body.appendChild(ta); 65 | ta.select(); 66 | document.execCommand('copy'); 67 | document.body.removeChild(ta); 68 | }, 69 | defaultMenuItems: [ 70 | { 71 | title: 'Copy Path', 72 | action: (d) => { 73 | MenuSupport.copyText(d.data.path); 74 | } 75 | }, 76 | { 77 | title: 'Open In Idea (Todo)', 78 | action: (d) => { 79 | // todo: add identify idea projects support 80 | window.open("jetbrains://open?url=" + d.data.path); 81 | } 82 | } 83 | ] 84 | } 85 | -------------------------------------------------------------------------------- /web/public/js/support/time-support.js: -------------------------------------------------------------------------------- 1 | let formatDate = function (d) { 2 | let unix_time = d * 1000; 3 | return standardFormatDate(unix_time); 4 | }; 5 | 6 | let standardFormatDate = function (d) { 7 | let date = new Date(d); 8 | let year = date.getUTCFullYear(); 9 | let month = date.getUTCMonth() + 1; 10 | let day = date.getUTCDate(); 11 | return year + "-" + month + "-" + day 12 | }; 13 | 14 | let buildYearOptions = function (date) { 15 | let startDate = new Date(date); 16 | let startYear = startDate.getFullYear(); 17 | let currentYear = new Date().getFullYear(); 18 | 19 | let yearOptions = []; 20 | for (let i = startYear; i <= currentYear; i++) { 21 | yearOptions.push(i); 22 | } 23 | return yearOptions; 24 | } 25 | --------------------------------------------------------------------------------