--- title: 'Unifying Konveyor' --- Unifying Konveyor project code conventions === [TOC] ## Problem statement Within number of Konveyor projects and their repositories both [upstream](https://github.com/orgs/konveyor/repositories) and [downstream](https://gitlab.cee.redhat.com/mig-integration) there are no common code conventions and development workflows that would ease the contribution process. Almost every project requires duplicated effort in automating very simple tasks such as creating development environment, building the source code, running simple test both locally and in the CI ecosystem, generating documentation, etc. Even minor things like documentation have different naming / paths and it's inconsistent e.g. development instrutions: [docs/Development.md](https://github.com/konveyor/pelorus/blob/master/docs/Development.md) vs [docs/hacking.md](https://github.com/konveyor/mig-operator/blob/master/docs/hacking.md) vs [README.md](https://github.com/konveyor/forklift-ui#development-scripts) vs [contributing.md](https://github.com/konveyor/move2kube/blob/main/contributing.md) and [code-of-conduct.md](https://github.com/konveyor/move2kube/blob/main/code-of-conduct.md) This leads to more work, maintenance and differences for the parts that could be similar. Also aspect of projects belonging to one community is reduced by those inconsistencies. ## Proposal Create a set of common conventions that could be used in automated fashion. For the parts that are not automated the docs should be available for developers to follow. # Execution There are couple of areas to address. Proposals are described below. Most of the scripting and development should be stored in one common repository that is available from both upstream and downstream, meaning that [GitHub](https://github.com/konveyor/) is the first choice to consider. ```mermaid graph GitHub_project --> common_repository; GitLab_project --> common_repository; ``` ## Common way of creating dev env Every developer who would like to contribute to the Konveyor project should be able to prepare local developer environment with all needed dev and test deps in similar way. Proposal is to have a common syntax invoked from the `Makefile` ``` # To create development environment make dev-env # To tear down developmen environment make clean-dev-env ``` We should include snippet for the Makefile together with supporting scripts that allows to install packages from different locations and packaging systems. e.g. `PyPI requirements.txt, test-requirements.txt, Go, dnf, GitHub released binaries, OpenShift CLIs, Ansible roles, collections, etc...` ## Common way of preparing test environment Simple static code analysis as well as simple test runs that could be ran on the local developer box as well as part of the CI/CD systems should have common way kind of ***entry point*** of running those. This allows to detach test logic from any system runner. It could be the same as previously mentioned `make dev-env`, but also something more specific to the test scenario. The repository that holds such test environments should be language agnostic and support different use cases that are adopted in the Konveyor set of projects. ## Common way of running various tests Each code analysis test as well test frameworks that could be shared *`NOTE that not all of them can be shared and those should live in the per project repository`* should be ran in a similar way. Proposal is similar to the dev-env and have common section/target invoked from the `Makefile`. One special target ***test*** should run all the tests that are required prior to submission code as the PR, example: ``` .PHONY tests tests: \ # code to run all tests via pre-commit. # This may be just a list of Makefile targets # 'pylava lint shellcheck' however running them # via pre-commit will allow to catch all the errors # at once rather then failing entire run on a first error. .PHONY: pylava lint shellcheck pylava: # code to run pylava via pre-commit lint: # code to run lint via pre-commit shellcheck: # code to run shellcheck via pre-commit ``` Having such entry point will allow us to invoke tests locally and inside CI/CD systems in a very same way, hiding logic behind the scenes by simply running: ``` # To run all tests make tests # Or if individual test run is required make shellcheck ``` ## Common configuration files for code analysis To ensure code is consistent across different Konveyor projects we should store configuration files for each of the linters/tests that will then be used in various projects. Those configuration files should be for various programming languages and selected analysis tools. Of course if the file contains information about project folder structure or files then only applicable rules should be common e.g.: ``` .ansible-lint pylava.ini pyproject.toml lintconf.yaml [...] ``` ## Carefully selected set of pre-commit hooks [pre-commit](https://pre-commit.com/) is a framework/package manager that allows to easilly install and run multi-language set of static code analysis tools agains the code. This framework can be also used to run a bit more comprehensive set of tests. There is a wide community supported set of [hooks](https://pre-commit.com/hooks.html) that we should agree on and enable for the applicable konveyor projects storing the configuration files in the common repository. If there are some missing code test runners or available one is not aligned with our requirements (`for example not accepted hook as it's modifying the source code`) we will write our own version and store in the common repository. We will store .pre-commit-config.yaml that lists *all* of the selected hooks for all the projects, with manual stage set. This stage will be commented out if the project needs it, e.g.: ``` - repo: https://github.com/golangci/golangci-lint rev: v1.44.2 hooks: - id: golangci-lint # stages: [manual] ``` Those hooks will be then wrapped around Makefile to allow common entry point accross different projects. # Example scenarios This section lists example scenarios and it's meant to explain better the entire concept. ### Running the tests via Makefile ```$ make tests``` ```mermaid flowchart LR CI["CI system (github actions/Praw/gitlab runner)"] --> |make tests|id1 Developer --> |make tests|id1 id1 --> |pre-commit run|pre-commit subgraph "Konveyor Project" id1{{Makefile}} end subgraph "pre-commit" id2{{pylint}} id3{{shellcheck}} id4{{other hook runner}} end ``` ### Running the tests prior to git push Action done via pre-commit hook installed by the ```pre-commit``` CLI ```mermaid flowchart LR subgraph Konveyor_project direction LR git-commit{{git commit}} --> id1 id1{{pre-commit github hook}} end subgraph pre-commit id2{{pylint}} id3{{shellcheck}} id4{{other hook runner}} end Konveyor_project --> |pre-commit run|pre-commit ``` ### Running individual test ```$ make shellcheck``` ```mermaid flowchart LR CI["CI system (github actions/Praw/gitlab runner)"] --> |make shellcheck|id1 Developer --> |make shellcheck|id1 id1 --> |pre-commit run shellcheck|pre-commit subgraph "Konveyor Project" id1{{Makefile}} end subgraph "pre-commit" id2{{shellcheck}} end ``` ### Code analysis config files Most files are stored in the common repository and synced. Possibly using some `Makefile sync-configs target`. Konveyor Project may even not contain those files and they will be synced before actual run in the test preparation step. Konveyor Project may also store a modified version of config file which is templated from common repository. ```mermaid flowchart LR subgraph Common_repository direction LR id1(".ansible-lint\npylava.ini\npyproject.toml\nlintconf.yaml\n[...]") end subgraph Konveyor_Project id2{{Makefile}} id3{{.ansible-lint\nlintconf.yaml}} end id1 -.-> |sync|id3 ``` ### Makefile and scripts `Makefile` and scripts which may live in the common repository should be synced with the Konveyor Project. All pre-check targets, even those that are not relevant for the programming language should be listed in the Makefile for easy sync. ***Note*** *the common_repository `Makefile` is a subset of Konveyor project* ```mermaid flowchart LR subgraph Common_repository direction LR id1("Makefile") id2("scipts/") end subgraph Konveyor_Project id3{{Makefile}} id4{{.ansible-lint\nlintconf.yaml}} id5{{scripts/}} end id1 -.-> |"sync (subset)"|id3 id2 -.-> |"sync"|id5 ``` ### Custom pre-commit hooks Similarly to any hook a custom hook will be written and stored in the common repository. Writing a custom hook is relatively easy task and a number of programming languages are supported. Please refer to the [creating new hooks ](https://pre-commit.com/index.html#new-hooks). Running a custom hook is same as other ones ```$ make customhook``` ```mermaid flowchart LR CI["CI system (github actions/Praw/gitlab runner)"] --> |make shellcheck|id1 Developer --> |make customhook|id1 id1 --> |pre-commit run customhook|pre-commit subgraph "Konveyor Project" id1{{Makefile}} end subgraph "pre-commit" id2{{shellcheck}} end ``` ### Putting everything together ```mermaid flowchart LR subgraph ida ["Konveyor GitHub projects"] direction LR subgraph id1a ["Pelorus"] direction LR id1b>"python, helm, shell, docs"] id1c{{"Makefile"}} end subgraph id2a ["Crane Operator"] direction LR id2b>"ansible, podman"] id2c{{"Makefile"}} end subgraph id3a ["Move2Kube"] direction LR id3b>"go, shell, podman"] id3c{{"Makefile"}} end subgraph id4a ["Other..."] direction LR id4b>"Fortran"] id4c{{"Makefile"}} end end subgraph idb ["Konveyor GitLab projects"] direction LR subgraph id5a ["minikube-cluster"] direction LR id5b>"ansible"] id5c{{"Makefile"}} end subgraph id6a ["oadp-operator-test"] direction LR id6b>"ansible, python"] id6c{{"Makefile"}} end subgraph id7a ["Other..."] direction LR id7b>"go"] id7c{{"Makefile"}} end end subgraph idc ["Common Repository"] id9a{{"Makefile"}} subgraph id9x ["Config files"] id9b(".ansible-lint\npylava.ini\npyproject.toml\nlintconf.yaml\ngolint.yaml\n[...]") end subgraph id9h ["Custom pre-check"] direction LR id9g>"fortran-check"] end subgraph id7a ["Other..."] direction LR id7b>"go"] id7c{{"Makefile"}} end end subgraph id10a ["pre-commit.com supported hooks"] direction LR id10b{{"ansible lint\ncheck yaml\ngolint\n[...]"}} end id9a -.-> |"sync"|ida id9a -.-> |"sync"|idb id9x -.-> |"sync"|ida id9x -.-> |"sync"|idb id9a --> |"custom target"|id9h id9a --> |"supported hooks"|id10a ```