Skip to content

Commit b30a4a7

Browse files
committed
fix: report generator
1 parent 951c125 commit b30a4a7

2 files changed

Lines changed: 163 additions & 15 deletions

File tree

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
name: Publish Cyclomatic Complexity Report
2+
description: For rate of production analysis, this action calculates code metrics on the repo and pushes the results to S3.
3+
inputs:
4+
aws-access-key-id:
5+
required: true
6+
aws-secret-access-key:
7+
required: true
8+
environment:
9+
required: true
10+
npm-token:
11+
required: true
12+
runs:
13+
using: 'composite'
14+
steps:
15+
- uses: actions/checkout@v3
16+
17+
- name: Use Node.js ${{ env.NODE_VERSION }}
18+
uses: actions/setup-node@v4
19+
with:
20+
node-version: ${{ env.NODE_VERSION }}
21+
cache: 'yarn'
22+
23+
- name: Configure package manager
24+
shell: sh
25+
run: |
26+
npm config set '//registry.npmjs.org/:_authToken' ${{ env.NPM_TOKEN }}
27+
yarn config set npmAuthToken ${{ env.NPM_TOKEN }}
28+
npm whoami
29+
env:
30+
NPM_TOKEN: ${{ inputs.npm-token }}
31+
32+
- name: Install dependencies
33+
id: install
34+
shell: bash
35+
run: yarn install --immutable
36+
37+
- uses: aws-actions/configure-aws-credentials@v1
38+
with:
39+
aws-access-key-id: ${{ inputs.aws-access-key-id }}
40+
aws-secret-access-key: ${{ inputs.aws-secret-access-key }}
41+
aws-region: us-east-1
42+
43+
- name: Generate Code Metrics for Commit
44+
id: metrics
45+
uses: offgriddev/cyclomatic-js-action@main
46+
with:
47+
github_token: ${{ github.token }}
48+
event: ${{ toJson(github.event) }}
49+
includedFileTypes: .js$
50+
excludedFileTypes: __mocks__|.test.js|Test.js|dist
51+
52+
- name: Print Result
53+
shell: bash
54+
run: cat ${{ steps.metrics.outputs.export_filename }}
55+
56+
- name: Publish Commit Analytics
57+
id: publish
58+
shell: bash
59+
run: |
60+
aws s3 sync complexity-assessment s3://cyclomatic-js-metrics-${{ inputs.environment }} --cache-control max-age=31536000

README.md

Lines changed: 103 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,123 @@
11
# CyclomaticJS - A Complexity Measure for JavaScript
22

3-
Cyclomatic Complexity was developed by a Computer Scientist at IBM named Thomas McCabe in an article titled [A Complexity Measure](https://ieeexplore.ieee.org/document/1702388). A metric we often use to identify "code quality" concerns, cyclomatic complexity is a useful analytical tool for many reasons. And now it's available for Vanilla JavaScript!
3+
Cyclomatic Complexity was developed by a Computer Scientist at IBM named Thomas McCabe in an article titled [A Complexity Measure](https://ieeexplore.ieee.org/document/1702388). A metric we often use to identify "code quality" concerns, cyclomatic complexity is a useful analytical tool for many reasons. And now it's available for both CommonJS and ESModules JavaScript repositories!
44

5-
If you're unfamiliar with Cyclomatic Complexity, it's an application of Graph Theory to the logical structure of algorithms. The logical structure of algorithms accounts for many constructs we leverage in software engineering: if-statements, switch-statements, for/while/forof/dowhile loops, etc. In measuring these facets of our source code, Cyclomatic Complexity measures _how many possible linear paths through a given algorithm_. It will produce a simple integer value as an assessment. For example, the following code has an objective Complexity of 2:
5+
First, let's cover the technical How-To for the contained NPM package and it's GitHub Action. Second, we'll dive deep into theory and explain why you would even want to use this anyway.
6+
7+
## Using
8+
9+
You can levarage CyclomaticJS as both a standalone import for your JavaScript project or as a GitHub Action in your CI workflows.
10+
11+
## CyclomaticJS as an Import
12+
13+
CyclomaticJS provides a library for importing into JS:
614

715
```javascript
8-
function main(a) {
9-
if (a > 0) {
10-
return "is positive"
16+
import {calculateComplexity} from 'cyclomatic-js'
17+
18+
const complexity = calculateComplexity('somefile.js')
19+
console.log(complexity)
20+
```
21+
22+
This will produce a file called `complexity-report.json` in the main directory of your repository. The file will loo like this:
23+
24+
```json
25+
[
26+
{
27+
"file": "./src/some-file.js",
28+
"report": {
29+
"function1": 2,
30+
"function2": 1,
31+
"function3": 3
32+
}
1133
}
34+
]
35+
```
1236

13-
return "is negative"
14-
}
37+
The report object is simple, it contains the filename and a `report` with the function name and the cyclomatic complexity for that function.
38+
39+
## CyclomaticJS as a GitHub Action
40+
41+
CyclomaticJS also provides a GitHub Action to generate a report for pushing to any BI tool or storage for aggregation and analysis:
42+
43+
```yml
44+
- name: Generate Code Metrics for Commit
45+
id: metrics
46+
uses: offgriddev/cyclomatic-js-action@main
47+
with:
48+
github_token: ${{ github.token }}
49+
event: ${{ toJson(github.event) }}
50+
includedFileTypes: .js$
51+
excludedFileTypes: __mocks__|.test.js|Test.js|dist
1552
```
1653
17-
As code grows, and I'm sure we've all seen it, algorithms can take on a life of their own and grow to contain numerous linear paths that all have to be maintained and amount to the cognitive overhead of a given algorithm.
54+
This action has one export: `export_filename`. This stores the filename for the report generated by the complexity reporter in the CyclomaticJS package.
1855

19-
This library provides any JS library the ability to analyze the Cyclomatic Complexity of their functions with a dependency solely on the [abstract-syntax-tree](https://www.npmjs.com/package/abstract-syntax-tree)
56+
## Why Use CyclomaticJS?
2057

21-
# How to Use
58+
To answer why you would want to use CyclomaticJS, you need to know a little about the code metric it uses. You may be new to software engineering, or you might be a senior or director and have not thoughtfully understood this concept. So let us briefly cover its nature.
2259

23-
CyclomaticJS has two export: `calculateComplexity` and `generateComplexityReport`
60+
### What is Cyclomatic Complexity
2461

25-
This function takes a `filename` and calculates the logical complexity of a file's contents.
62+
Cyclomatic Complexity, the code metric this repository analyses, measures the number of linear paths through a given function. Your function may be simple:
2663

2764
```javascript
28-
import { calculateComplexity } from 'cyclomatic-js'
65+
function getNumberPolarity(a) {
66+
if (a > 0) {
67+
return 'is positive'
68+
} else if (a < 0) {
69+
return 'is negative'
70+
} else {
71+
return 'is neither positive nor negative'
72+
}
73+
}
74+
```
2975

30-
const complexity = calculateComplexity('somefile.js')
76+
But as you can see, given a specific range of the variable `a`, the output may be different. There are three possible outputs.
3177

32-
```
78+
1. When `a > 0`, the output is `is positive`.
79+
2. When `a < 0`, the output is `is negative`.
80+
3. When `a == 0`, the output is `is neither positive nor negative`.
81+
82+
Since there are 3 possible paths through this function, the Cyclomatic Complexity is 3. This is acquired by creating an Abstract Syntax Tree and measuring the _logical structures_ of a function. An `IfStatement`, for example, increases the Cyclomatic Complexity of the function because it creates a _logical branch_ in it. The lowest possible number of logical branches in a given function is 1 as an empty `BlockStatement`, or `function empty() { }` has one logical branch.
83+
84+
### Logical Structures that Increase Complexity
85+
86+
These logical structures are Universal. Any Turing Complete language can be measured by its logical structures. The following statements and expressions unconditionally increase the complexity of a function:
87+
88+
`IfStatement`
89+
`TryStatement`
90+
`CatchClause`
91+
`DoWhileStatement`
92+
`ForInStatement`
93+
`ForOfStatement`
94+
`WhileStatement`
95+
`ConditionalExpression`
96+
97+
The following conditionally increase the complexity of a function:
98+
99+
`SwitchCase` as in the `case` statement of a `SwitchStatement` increases the complexity when the `case` has statements.
100+
`LogicalExpression` increases the complexity when it is a `||`, `&&`, or `??`.
101+
102+
### Why Use Cyclomatic Complexity
103+
104+
Now for the question you may have asked yourself at some point if you've made it this far and have been looking for a JavaScript library to do this analysis for you. There are two main personas this library and action are targetting: Developers and anyone concerned with _estimations_. That last persona is vague. I promise, I will get there.
105+
106+
From a developer perspective, you may be concerned with Code Quality and Maintainability. You may have linting rules setup to limit the "size" if your functions, e.g. [eslint-complexity](https://eslint.org/docs/latest/rules/complexity). Some developers have a genuine interest in this as an engineering concern, and rightfully so. This may be around modularization and the role of writing more simple and elegant functions.
107+
108+
If you don't believe Cyclomatic Complexity or nerd out on analysing the logical complexity of your code, then you may be concerned about Estimations in some sense. You might be a Project Manager, a Lead Engineer, or perhaps you work in Professional Services and are putting a bid on a job and want to be competitive _and_ realistic.
109+
110+
That's right, you might want to give a _realistic estimation for a given software development effort_. You might, for example, be the CTO and need to report to ELT a high-level estimate for getting funding for a project. The second category is broad, but you want to provide as realistic of an estimation as you can.
111+
112+
## Estimation Methodology
113+
114+
Code is complex. Complexity in our functions is a result of the logical structure we write. When we develop, we produce functions, algorithms, that codify _business requirements_. If we take snapshots of this over time, we can understand the pace at which we develop software. Therefore, we can provide realistic estimations based on the concrete code we deliver over time.
115+
116+
This repository allows your teams to analyze the comlexity you're deliver as part of codifying the business requirements in your repositories. Complexity grows over time. Each developer also delivers functions of a given complexity at some rate. Cyclomatic Complexity is the objective measurement you can use the give estimations. By measuring the complexity a developer commits to production, you can do the following:
117+
118+
1. The actual complexity delivered by getting the delta from the previous commit
119+
2. Validate the accuracy of an estimate
120+
3. Understand the rate a developer produces business requirements based on the developer's historic delivery of functional complexity
33121

34122
# Supported Source Code
35123

0 commit comments

Comments
 (0)