Moose-CI runs automatic Moose analyses on software projects.
Disclaimer: This project is at a very early stage. It only has a few features and may contain bugs.
You can request features in the issues tab.
Some of features it should offer:
- run alone ("headless" mode)
- load a project (either from command line or in a configuration file)
- accept projects inany programming language that Moose can handle (C, Java, Pharo, Python, Typescript,...)
- run a list of analyses (either "standard" ones, or some specified in a configuration file)
- output the results as JSON and console text
- output the results in other formats (XML, CSV, HTML,...)
Some metrics and analyses that are envisionned:
- size (LOC, # of classes,...)
- report number of entities that fail the rules
- list too big classes (based on LOC or # members)
- list too big methods/functions (based on LOC)
- list of too complex methods/functions (based on cyclomatic complexity)
- list of too large method/function APIs (# number of parameters)
- packages/modules in cyclic dependencies
- list of code clones
In would be good to be able to output some visualizations already available in Moose:
- DSM (Dependency Structure Matrix)
- Architectural view
- System complexity
- Distribution map
The easiest way to run Moose-CI is with Docker. You do not need Pharo or any other dependency.
First, pull the image:
docker pull ghcr.io/moosetechnology/moose-ci:latestFrom the project directory, initialize it as a Moose-CI project. This creates a moose-ci.ston config file that you can customize:
docker run -v "$(pwd):/src" ghcr.io/moosetechnology/moose-ci:latest initThen run the analysis with the analyze command:
docker run -v "$(pwd):/src" ghcr.io/moosetechnology/moose-ci:latest analyzeYou can also run Moose-CI on a project without initializing a config file by passing the project path:
docker run -v /path/to/your/project:/src ghcr.io/moosetechnology/moose-ci:latest analyze /srcThe project is mounted in the container at /src, so you need to pass that path to the analyze command.
init: create a new moose-ci config file.analyze: analyze the current directory using the existing config file.analyze <project-path>: analyze the project.
Running analyze prints the report to the console and writes it to files in the output directory (.moose-ci/report/ by default, relative to the analyzed project).
By default a JSON report is written to report-<TIMESTAMP>.json:
{
"metrics" : {
"files" : 8,
"loc" : 5,
"packages" : 2,
"classes" : 3
},
"violations" : [
{
"rule" : "No docstring",
"severity" : "Hint",
"count" : 3,
"entities" : [
{
"entity" : "DummyClass",
"file" : "relative/path/to/no_docstring.py",
"startLine" : 1,
"endLine" : 2
}
]
}
]
}Violations are grouped by rule. Each group contains the rule name, its severity, the number of violating entities and the details of each entity (name, file and source lines).
You can configure the output in moose-ci.ston:
#outputFormats : [
#json
],
#outputPath : '.moose-ci/report'#outputFormats: list of formats to write.#jsonis the only format available for now.#outputPath: directory (relative to the project) where the report files are written.
Moose-CI uses a moose-ci.ston config file. Run moose-ci init to create one.
You can configure the report output formats and location here too (see Report output).
You can update the rules list and customize each rule's threshold:
...
#rules : [
#long_file: 1000,
#no_docstring,
#too_many_parameters: 10
]
...You can also choose which metrics to compute:
...
#metrics : [
#files,
#loc,
#packages,
#classes
]
...Metrics can be omitted (or set to an empty list) if you do not want to compute any metric.
| Key | Threshold | Description |
|---|---|---|
#long_file |
1000 | Reports files whose number of lines of code exceeds the threshold. |
#no_docstring |
N/A | Reports functions, methods and classes that are missing a docstring. |
#too_many_parameters |
10 | Reports methods whose number of parameters exceeds the threshold. |
#large_class |
20 | Reports classes that have too many methods or attributes. |
#local_var_naming |
N/A | Reports local variables and parameters whose names do not respect the naming convention. |
#unused_local_variable |
N/A | Reports local variables that are written but never read. |
#unused_parameter |
N/A | Reports function and method parameters that are never used. |
#unused_private_method |
N/A | Reports class-private methods that are never invoked. |
#shadowed_attribute |
N/A | Reports attributes whose name duplicates their containing class name. |
#function_naming |
N/A | Reports functions whose names do not comply with the naming convention. |
| Key | Description | Applicable languages |
|---|---|---|
#files |
Number of source files. | all |
#loc |
Total lines of code. | all |
#packages |
Number of packages. | all |
#classes |
Number of classes. | all |
You can run MooseCI in CI with the Setup MooseCI GitHub Action. Add the action to a workflow:
name: MooseCI
on: [push, pull_request]
permissions:
contents: read
actions: write
pull-requests: write
jobs:
analyze:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: moosetechnology/setup-MooseCI@main
with:
project-path: .project-path: the folder to analyze, relative to the workspace. Default:.actions: writeis needed to upload the report artifact.pull-requests: writeis needed to comment the report link on pull requests.- The
moose-ci.stonconfig file must be placed inside the analyzed project folder (see Configuration). - On pull requests, the action comments the report download URL and the analysis summary on the PR.
Load MooseCI in a Moose image with Metacello:
Metacello new
baseline: 'MooseCI';
repository: 'github://moosetechnology/MooseCI:master/src';
load.A rule is a subclass of MCIAbstractQualityRule. It must implement:
class >> key: a unique identifier, e.g.#too_many_linesclass >> defaultThreshold: the threshold used when none is set in the configcontextFilterBlock: a block that selects the entities to analyzequeryHandler: a block that decides what counts as a violation
Example of a simple rule that reports files with too many lines of code:
MCIAbstractQualityRule << #MCITooManyLinesRule
slots: {};
package: 'MooseCI-QualityRules'
MCITooManyLinesRule class >> key [
^ #too_many_lines
]
MCITooManyLinesRule class >> defaultThreshold [
^ 500
]
MCITooManyLinesRule >> contextFilterBlock [
^ [ :collection | collection select: [ :each | each isModule ] ]
]
MCITooManyLinesRule >> queryHandler [
^ FamixCBQueryHandler on: (FQSelectScriptQuery script: [ :entity |
entity numberOfLinesOfCode > self threshold ])
]You can also override ruleName, defaultSeverity, applicableLanguages and description. applicableLanguages is #all by default. You can specify it by passing a list of languages, e.g. #( #python ). Rules that do not use a threshold return -1 as their defaultThreshold (e.g. #no_docstring).
New rules are discovered automatically from their key, so no registration is needed. To use a rule, add its key to the #rules list in moose-ci.ston:
#rules : [
#too_many_lines: 300,
#no_docstring
]#too_many_lines: 300: enables the rule with a custom threshold.#no_docstring: enables a rule that has no threshold.
To make a rule part of the default config created by moose-ci init, add its key to MCIQualityRules class >> pythonRules (or javaRules / defaultRules).
A metric is a subclass of MCIAbstractMetric. It must implement:
class >> key: a unique identifier, e.g.#methodscompute:: a method that computes the value from a Moose model
Example of a simple metric that counts the number of methods:
MCIAbstractMetric << #MCINumberOfMethodsMetric
slots: {};
package: 'MooseCI-Metrics'
MCINumberOfMethodsMetric class >> key [
^ #methods
]
MCINumberOfMethodsMetric >> compute: aModel [
^ aModel allMethods size
]You can also override metricName, description and applicableLanguages. metricName defaults to the key as a string. applicableLanguages is #all by default. You can specify it by passing a list of languages, e.g. #( #python ).
New metrics are discovered automatically from their key, so no registration is needed. To use a metric, add its key to the #metrics list in moose-ci.ston:
#metrics : [
#methods,
#loc
]Metrics can be empty (#metrics : [ ]) or omitted entirely when you do not want to compute any metric.
To make a metric part of the default config created by moose-ci init, add its key to MCIMetrics class >> pythonMetrics (or javaMetrics / defaultMetrics).