|
| 1 | +# Breaking down coala |
| 2 | + |
| 3 | +| Metadata | | |
| 4 | +|----------|-----------------------------------------------| |
| 5 | +| cEP | 0023 | |
| 6 | +| Version | 1.0 | |
| 7 | +| Title | Breaking down the coala | |
| 8 | +| Authors | Muhammad Kaisar Arkhan <mailto:[email protected]> | |
| 9 | +| Status | Proposed | |
| 10 | +| Type | Feature | |
| 11 | + |
| 12 | +## Abstract |
| 13 | + |
| 14 | +This cEP proposes a new architecture evolution for coala. |
| 15 | + |
| 16 | +## How coala works |
| 17 | + |
| 18 | +coala is a Python program that also provides API for bears and interacting with |
| 19 | +them. |
| 20 | + |
| 21 | +Bears are Python classes filled with metadata needed to execute linter programs |
| 22 | +and may contain helper code to generate configuration files. Since bears are |
| 23 | +Python code, some bears have the actual linter inside of them and some just |
| 24 | +execute the Python API of a linter. |
| 25 | + |
| 26 | +Bears are written with the API provided by the coala core and they're running |
| 27 | +under the same process as coala. |
| 28 | + |
| 29 | +On startup, coala reads and intepret the configuration file. Configuration files |
| 30 | +are separated by sections which contains what bear is required and the |
| 31 | +configuration for the bear. |
| 32 | + |
| 33 | +After it's intepreted, it is passed over to an executor which will executes it |
| 34 | +per section. The executor will find and import the bear class which usually |
| 35 | +resides in the Python packages path. It then pass the Section data to the bear |
| 36 | +and set it's require configuration. The bear first prepare the whole command |
| 37 | +along with it's parameters or generate a configuration file. After that's done, |
| 38 | +the linter program will be executed by the bear as a separate process and the |
| 39 | +bear will intepret it's standard output by matching a regex. The bear will yield |
| 40 | +the output of the program to the executor which will end up to the generator |
| 41 | +where the user see the warning/error and determine an action. |
| 42 | + |
| 43 | +## The Architectural Issue |
| 44 | + |
| 45 | +There is no clear distinction between the coala core which job is to intepret |
| 46 | +and run bears and the one that shows the command line interface to the user. In |
| 47 | +addition, bears in essence can do anything it wants to since it is just a Python |
| 48 | +class that is loaded into the coala process. |
| 49 | + |
| 50 | +This caused numerous unwanted regressions between bears and coala and costed a |
| 51 | +lot in maintenance. This is also the reason why bears are not released |
| 52 | +separately as a "collection" rather it is shipped with coala core along with |
| 53 | +every linter in the collection. |
| 54 | + |
| 55 | +Instead of leaving the project at this state, we should define borders and setup |
| 56 | +a more proper architecture and standards. |
| 57 | + |
| 58 | +## The new architecture |
| 59 | + |
| 60 | +Bears are now just simple formatted plain-text file containing the metadata |
| 61 | +needed. It can also contain a simple program as helpers or sole linters. |
| 62 | + |
| 63 | +coala will separated into two parts: The coala Engine and The coala CLI. |
| 64 | + |
| 65 | +The coala CLI's sole job is to present an interactive command line interface for |
| 66 | +the coala Engine. The CLI will present the output of the Engine and present them |
| 67 | +with possible actions that the user can do. |
| 68 | + |
| 69 | +Meanwhile, the coala Engine's sole job is to intepret the bear files and |
| 70 | +execute/supervise the linter/helper programs along with committing the action |
| 71 | +that is requsted by the user. |
| 72 | + |
| 73 | +If a helper program is needed, They'll act as the linter program and do stuff |
| 74 | +before the program (e.g generating the configuration file) and do stuff |
| 75 | +after it (e.g cleaning). |
| 76 | + |
| 77 | +The frontend and backend programs will communicate to each other via json-rpc. |
| 78 | +json-rpc is chosen for it's simplicity. |
| 79 | + |
| 80 | +## The new bear file |
| 81 | + |
| 82 | +Bears will be ini files which is already a part of the Python standard library. |
| 83 | +Ini files are readable and simple to write. Making it a lot easier for projects |
| 84 | +making their own internal bears. |
| 85 | + |
| 86 | +### GoVet.bear |
| 87 | + |
| 88 | +```ini |
| 89 | +[bear] |
| 90 | +name = GoVetBear |
| 91 | +description = Analyze Go code and raise suspicious constructs, such as printf |
| 92 | + calls whose arguments do not correctly match the format string, |
| 93 | + useless assignments, common mistakes about boolean operations, unreachable code, |
| 94 | + etc. |
| 95 | +languages = Go |
| 96 | +authors = The coala developers |
| 97 | + |
| 98 | +license = AGPL-3.0 |
| 99 | +can_detect = Unused code, Smell, Unreachable Code |
| 100 | + |
| 101 | +[requirement] |
| 102 | +type = go |
| 103 | +package = golang.org/cmd/vet |
| 104 | + |
| 105 | +[run] |
| 106 | +executable = go |
| 107 | +arguments = vet |
| 108 | +use_stdout = false |
| 109 | +use_stderr = true |
| 110 | +output_regex = .+:(?P<line>\d+): (?P<message>.*) |
| 111 | +``` |
| 112 | + |
| 113 | +The coala Engine will intepret the following file, check the requirement, and |
| 114 | +execute the linter program while intepreting the output with the regex. |
| 115 | + |
| 116 | +### SpaceConsistency.bear |
| 117 | + |
| 118 | +```ini |
| 119 | +[bear] |
| 120 | +name = SpaceConsistencyBear |
| 121 | +description = Check and correct spacing for all textual data. This includes usage of |
| 122 | + tabs vs. spaces, trailing whitespace and (missing) newlines before |
| 123 | + the end of the file. |
| 124 | +languages = All |
| 125 | +authors = The coala developers |
| 126 | + |
| 127 | +license = AGPL-3.0 |
| 128 | +can_detect = Formatting |
| 129 | + |
| 130 | +[param.use_spaces] |
| 131 | +description = True if spaces are to be used instead of tabs |
| 132 | +type = boolean |
| 133 | + |
| 134 | +[param.allow_trailing_whitespace] |
| 135 | +description = Whether to allow trailing whitespace or not. |
| 136 | +type = boolean |
| 137 | +default = false |
| 138 | + |
| 139 | +[params.indent_size] |
| 140 | +description = Number of spaces per indentation level |
| 141 | +type = int |
| 142 | +default = 8 |
| 143 | + |
| 144 | +[params.enforce_newline_at_EOF] |
| 145 | +description = Whether to enforce a newline at the end of file |
| 146 | +type = boolean |
| 147 | +default = true |
| 148 | +config_key = enforce_newline |
| 149 | + |
| 150 | +[run] |
| 151 | +local = true |
| 152 | +executable = space_consistency_bear.py |
| 153 | +output_regex = .+:(?P<line>\d+): (?P<message>.*) |
| 154 | + |
| 155 | +``` |
| 156 | + |
| 157 | +The coala Engine will intepret the following file, setup the command arguments, |
| 158 | +and run the Python file. Note that it does not load it as a library rather |
| 159 | +running it as a program. |
| 160 | + |
| 161 | +This also means it is possible to run it without the engine. |
| 162 | + |
| 163 | +``` |
| 164 | +/usr/local/share/coala/bears/SpaceConsistency/space_consistency_bear.py \ |
| 165 | + --use_spaces=false \ |
| 166 | + --allow_trailing_whitespace=false \ |
| 167 | + --indent_size=8 \ |
| 168 | + --enforce_newline_at_EOF=8 \ |
| 169 | +``` |
| 170 | + |
| 171 | +## The RPC |
| 172 | + |
| 173 | +The RPC will follow the JSON-RPC specification |
| 174 | +(https://www.jsonrpc.org/specification). |
| 175 | + |
| 176 | +The methods will be the following: |
| 177 | + * `start` |
| 178 | + * `commit` |
| 179 | + |
| 180 | +The following examples will use the syntax below: |
| 181 | +``` |
| 182 | +--> data sent to the Engine |
| 183 | +<-- data sent to the Frontend |
| 184 | +``` |
| 185 | + |
| 186 | +### `start_session` |
| 187 | + |
| 188 | +`start_session` will start a new coala session. It will pass the whole |
| 189 | +configuration. |
| 190 | + |
| 191 | +```json |
| 192 | +--> { |
| 193 | + "jsonrpc": "2.0", |
| 194 | + "method": "start_session", |
| 195 | + "params": { |
| 196 | + "sections": [ |
| 197 | + "all": { |
| 198 | + "bears": ["SpaceConsistencyBear"], |
| 199 | + "use_spaces": true, |
| 200 | + "files": "**/**.py" |
| 201 | + } |
| 202 | + ] |
| 203 | + } |
| 204 | + } |
| 205 | +<-- { |
| 206 | + "jsonrpc": "2.0", |
| 207 | + "result": { |
| 208 | + "file": "/home/asuka/src/myproject/main.py", |
| 209 | + "bear": "SpaceConsistencyBear", |
| 210 | + "message": "Tabs used instead of spaces.", |
| 211 | + "line": 47, |
| 212 | + "patch": "<patch goes here>", |
| 213 | + "has_next": true, |
| 214 | + "actions": [ |
| 215 | + { |
| 216 | + "action": "patch", |
| 217 | + "name": "Apply Patch", |
| 218 | + "description": "Applies the proposed patch to the file" |
| 219 | + }, |
| 220 | + { |
| 221 | + "action": "ignore", |
| 222 | + "name": "Ignore", |
| 223 | + "description": "Ignore the warning" |
| 224 | + } |
| 225 | + ] |
| 226 | + } |
| 227 | + } |
| 228 | +``` |
| 229 | + |
| 230 | +### `commit` |
| 231 | + |
| 232 | +`commit` will commit an action proposed by the Engine and pass onto the next warning. |
| 233 | + |
| 234 | +```json |
| 235 | +--> { |
| 236 | + "jsonrpc": "2.0", |
| 237 | + "method": "commit", |
| 238 | + "params": { |
| 239 | + "action": "ignore" |
| 240 | + } |
| 241 | + } |
| 242 | +<-- { |
| 243 | + "jsonrpc": "2.0", |
| 244 | + "result": { |
| 245 | + "file": "/home/asuka/src/myproject/handler.py", |
| 246 | + "bear": "SpaceConsistencyBear", |
| 247 | + "message": "Tabs used instead of spaces.", |
| 248 | + "line": 20, |
| 249 | + "patch": "<patch goes here>", |
| 250 | + "section": "all", |
| 251 | + "has_next": false, |
| 252 | + "actions": [ |
| 253 | + { |
| 254 | + "action": "patch", |
| 255 | + "name": "Apply Patch", |
| 256 | + "description": "Applies the proposed patch to the file" |
| 257 | + }, |
| 258 | + { |
| 259 | + "action": "ignore", |
| 260 | + "name": "Ignore", |
| 261 | + "description": "Ignore the warning" |
| 262 | + } |
| 263 | + ] |
| 264 | + } |
| 265 | + } |
| 266 | +--> { |
| 267 | + "jsonrpc": "2.0", |
| 268 | + "method": "commit", |
| 269 | + "params": { |
| 270 | + "action": "ignore" |
| 271 | + } |
| 272 | + } |
| 273 | +<-- { |
| 274 | + "jsonrpc": "2.0", |
| 275 | + "result": {} |
| 276 | + } |
| 277 | +``` |
0 commit comments