- Development Mode
- Available Scripts
- Config
- Building For Production
- Debugging in VSCode
- Messages from MegaLinter server
- Release Management
- Clone this repo
- Run
npm i
to install dependencies - Start megalinter server
npm run start:megalinter
- Run
npm start
- Open
codetotal app
npm start
start all projects in development modenpm run lint
lint all projectsnpm run build:be
build the backend (used bylaunch.json
file for debugging)npm run start:fe
start the FE in development modenpm test
run all unit tests
Add a .env
file in the root of the project.
Only variables starting with the CODETOTAL_
prefix will be injected into the frontend bundle.
# MEGALINTER
MEGALINTER_ANALYSIS_URL=http://127.0.0.1:8000/analysis
MEGALINTER_UPLOAD_URL=http://127.0.0.1:8000/upload-file
MEGALINTER_REDIS_URL=redis://127.0.0.1:6379
MEGALINTER_REDIS_CHANNEL=megalinter:pubsub:<request-id>
# BACKEND
CODETOTAL_HTTP_PORT=8081
CODETOTAL_HTTP_HOST=127.0.0.1
CODETOTAL_WS_PORT=8080
CODETOTAL_WS_HOST=127.0.0.1
DEBUG_MODULES=actions,megalinter,stores,transport
# FRONTEND
CODETOTAL_UPLOAD_FILE_LIMIT_BYTES=10000000
- Config files must be set before the build (see
Config
section) - Run
npm run build
at the root folder - This will create a
dist
folder with the backend code - The frontend code will be under
dist/public
- Run using
npm run production
(equivalent tonode dist/index.js
)
Add a launch.json
file under the .vscode
folder with the following content:
{
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Build Project",
"program": "${workspaceFolder}/packages/backend/dist/index.js",
"envFile": "${workspaceFolder}/.env",
"preLaunchTask": "npm: build:be",
"sourceMaps": true,
"smartStep": true,
"internalConsoleOptions": "openOnSessionStart",
"outFiles": ["${workspaceFolder}/packages/backend/dist/**/*.js"]
}
]
}
Event-based management of MegaLinter Server can send the following list of messages into Redis PUBSUB channel built the following way: megalinter:pubsub:REQUEST_ID
Example: megalinter:pubsub:RQ_cbdd1d82-1e7b-11ee-9258-0242ac120004
This message is sent once by analysis, when a MegaLinter instance has identified the linters to run and is about to start them.
Example of megalinterStart messageType:
{
"messageType": "megalinterStart",
"megaLinterStatus": "created",
"linters": [
{
"descriptorId": "BASH",
"linterId": "bash-exec",
"linterKey": "BASH_EXEC",
"linterVersion": "5.2.15",
"linterCliLintMode": "file",
"requestId": "RQ_cbdd1d82-1e7b-11ee-9258-0242ac120004",
"docUrl": "https://megalinter.io/alpha/descriptors/bash_bash_exec",
"iconPngUrl": "https://raw.githubusercontent.com/oxsecurity/megalinter/main/docs/assets/icons/linters/bash-exec.png",
"isFormatter": false,
"isSBOM": false,
"filesNumber": 1
},
{
"descriptorId": "BASH",
"linterId": "shellcheck",
"linterKey": "BASH_SHELLCHECK",
"linterVersion": "0.9.0",
"linterCliLintMode": "list_of_files",
"requestId": "RQ_cbdd1d82-1e7b-11ee-9258-0242ac120004",
"docUrl": "https://megalinter.io/alpha/descriptors/bash_shellcheck",
"iconPngUrl": "https://raw.githubusercontent.com/oxsecurity/megalinter/main/docs/assets/icons/linters/shellcheck.png",
"isFormatter": false,
"isSBOM": false,
"filesNumber": 1
},
{
"descriptorId": "BASH",
"linterId": "shfmt",
"linterKey": "BASH_SHFMT",
"linterVersion": "ERROR",
"linterCliLintMode": "list_of_files",
"requestId": "RQ_cbdd1d82-1e7b-11ee-9258-0242ac120004",
"docUrl": "https://megalinter.io/alpha/descriptors/bash_shfmt",
"iconPngUrl": "https://raw.githubusercontent.com/oxsecurity/megalinter/main/docs/assets/icons/linters/shfmt.png",
"isFormatter": true,
"isSBOM": false,
"filesNumber": 1
},
{
"descriptorId": "COPYPASTE",
"linterId": "jscpd",
"linterKey": "COPYPASTE_JSCPD",
"linterVersion": "ERROR",
"linterCliLintMode": "project",
"requestId": "RQ_cbdd1d82-1e7b-11ee-9258-0242ac120004",
"docUrl": "https://megalinter.io/alpha/descriptors/copypaste_jscpd",
"isFormatter": false,
"isSBOM": false
},
{
"descriptorId": "REPOSITORY",
"linterId": "checkov",
"linterKey": "REPOSITORY_CHECKOV",
"linterVersion": "3.11",
"linterCliLintMode": "project",
"requestId": "RQ_cbdd1d82-1e7b-11ee-9258-0242ac120004",
"docUrl": "https://megalinter.io/alpha/descriptors/repository_checkov",
"iconPngUrl": "https://raw.githubusercontent.com/oxsecurity/megalinter/main/docs/assets/icons/linters/checkov.png",
"isFormatter": false,
"isSBOM": false
},
{
"descriptorId": "REPOSITORY",
"linterId": "devskim",
"linterKey": "REPOSITORY_DEVSKIM",
"linterVersion": "1.0.11",
"linterCliLintMode": "project",
"requestId": "RQ_cbdd1d82-1e7b-11ee-9258-0242ac120004",
"docUrl": "https://megalinter.io/alpha/descriptors/repository_devskim",
"iconPngUrl": "https://raw.githubusercontent.com/oxsecurity/megalinter/main/docs/assets/icons/linters/devskim.png",
"isFormatter": false,
"isSBOM": false
},
{
"descriptorId": "REPOSITORY",
"linterId": "dustilock",
"linterKey": "REPOSITORY_DUSTILOCK",
"linterVersion": "1.2.0",
"linterCliLintMode": "project",
"requestId": "RQ_cbdd1d82-1e7b-11ee-9258-0242ac120004",
"docUrl": "https://megalinter.io/alpha/descriptors/repository_dustilock",
"iconPngUrl": "https://raw.githubusercontent.com/oxsecurity/megalinter/main/docs/assets/icons/linters/dustilock.png",
"isFormatter": false,
"isSBOM": false
},
{
"descriptorId": "REPOSITORY",
"linterId": "git_diff",
"linterKey": "REPOSITORY_GIT_DIFF",
"linterVersion": "2.38.5",
"linterCliLintMode": "project",
"requestId": "RQ_cbdd1d82-1e7b-11ee-9258-0242ac120004",
"docUrl": "https://megalinter.io/alpha/descriptors/repository_git_diff",
"isFormatter": false,
"isSBOM": false
},
{
"descriptorId": "REPOSITORY",
"linterId": "gitleaks",
"linterKey": "REPOSITORY_GITLEAKS",
"linterVersion": "8.17.0",
"linterCliLintMode": "project",
"requestId": "RQ_cbdd1d82-1e7b-11ee-9258-0242ac120004",
"docUrl": "https://megalinter.io/alpha/descriptors/repository_gitleaks",
"iconPngUrl": "https://raw.githubusercontent.com/oxsecurity/megalinter/main/docs/assets/icons/linters/gitleaks.png",
"isFormatter": false,
"isSBOM": false
},
{
"descriptorId": "REPOSITORY",
"linterId": "kics",
"linterKey": "REPOSITORY_KICS",
"linterVersion": "1.7.3",
"linterCliLintMode": "project",
"requestId": "RQ_cbdd1d82-1e7b-11ee-9258-0242ac120004",
"docUrl": "https://megalinter.io/alpha/descriptors/repository_kics",
"iconPngUrl": "https://raw.githubusercontent.com/oxsecurity/megalinter/main/docs/assets/icons/linters/kics.png",
"isFormatter": false,
"isSBOM": false
},
{
"descriptorId": "REPOSITORY",
"linterId": "secretlint",
"linterKey": "REPOSITORY_SECRETLINT",
"linterVersion": "7.0.3",
"linterCliLintMode": "project",
"requestId": "RQ_cbdd1d82-1e7b-11ee-9258-0242ac120004",
"docUrl": "https://megalinter.io/alpha/descriptors/repository_secretlint",
"iconPngUrl": "https://raw.githubusercontent.com/oxsecurity/megalinter/main/docs/assets/icons/linters/secretlint.png",
"isFormatter": false,
"isSBOM": false
},
{
"descriptorId": "REPOSITORY",
"linterId": "semgrep",
"linterKey": "REPOSITORY_SEMGREP",
"linterVersion": "1.31.2",
"linterCliLintMode": "project",
"requestId": "RQ_cbdd1d82-1e7b-11ee-9258-0242ac120004",
"docUrl": "https://megalinter.io/alpha/descriptors/repository_semgrep",
"iconPngUrl": "https://raw.githubusercontent.com/oxsecurity/megalinter/main/docs/assets/icons/linters/semgrep.png",
"isFormatter": false,
"isSBOM": false
},
{
"descriptorId": "REPOSITORY",
"linterId": "trivy",
"linterKey": "REPOSITORY_TRIVY",
"linterVersion": "0.43.1",
"linterCliLintMode": "project",
"requestId": "RQ_cbdd1d82-1e7b-11ee-9258-0242ac120004",
"docUrl": "https://megalinter.io/alpha/descriptors/repository_trivy",
"iconPngUrl": "https://raw.githubusercontent.com/oxsecurity/megalinter/main/docs/assets/icons/linters/trivy.png",
"isFormatter": false,
"isSBOM": false
},
{
"descriptorId": "SPELL",
"linterId": "cspell",
"linterKey": "SPELL_CSPELL",
"linterVersion": "ERROR",
"linterCliLintMode": "list_of_files",
"requestId": "RQ_cbdd1d82-1e7b-11ee-9258-0242ac120004",
"docUrl": "https://megalinter.io/alpha/descriptors/spell_cspell",
"isFormatter": false,
"filesNumber": 1,
"isSBOM": false
}
],
"requestId": "RQ_cbdd1d82-1e7b-11ee-9258-0242ac120004"
}
This message is sent once by linter run, it indicates that a single linter has started running.
Example of linterStart messageType with linterCliLintMode=project
:
{
"messageType": "linterStart",
"linterStatus": "started",
"descriptorId": "REPOSITORY",
"linterId": "trivy",
"linterKey": "REPOSITORY_TRIVY",
"linterVersion": "0.43.1",
"linterCliLintMode": "project",
"requestId": "RQ_cbdd1d82-1e7b-11ee-9258-0242ac120004",
"docUrl": "https://megalinter.io/alpha/descriptors/repository_trivy",
"isFormatter": false,
"isSBOM": false
}
Example of linterStart messageType with linterCliLintMode=list_of_files
(with filesNumber additional property):
{
"messageType": "linterStart",
"linterStatus": "started",
"descriptorId": "BASH",
"linterId": "shellcheck",
"linterKey": "BASH_SHELLCHECK",
"linterVersion": "0.9.0",
"linterCliLintMode": "list_of_files",
"requestId": "RQ_cbdd1d82-1e7b-11ee-9258-0242ac120004",
"docUrl": "https://megalinter.io/alpha/descriptors/bash_shellcheck",
"isFormatter": false,
"isSBOM": false,
"filesNumber": 1
}
This message is sent once by linter run, it indicates that a single linter has been completed.
If SARIF is available, it will be located in outputSarif
property, otherwise results can be found in outputText
and or outputJson
.
Example of linterComplete messageType:
{
"messageType": "linterComplete",
"linterStatus": "success",
"linterErrorNumber": 0,
"linterStatusMessage": "No errors were found in the linting process",
"linterElapsedTime": 11.16,
"linterCliCommand": [
"trivy",
"fs",
"--scanners",
"vuln,config",
"--exit-code",
"1",
"--format",
"sarif",
"-o",
"/tmp/ct-megalinter-xmv82bvyv/megalinter-reports/sarif/REPOSITORY_TRIVY.sarif",
"."
],
"descriptorId": "REPOSITORY",
"linterId": "trivy",
"linterKey": "REPOSITORY_TRIVY",
"linterVersion": "0.43.1",
"linterCliLintMode": "project",
"requestId": "RQ_cbdd1d82-1e7b-11ee-9258-0242ac120004",
"docUrl": "https://megalinter.io/alpha/descriptors/repository_trivy",
"isFormatter": false,
"isSBOM": false,
"outputSarif": {
"$schema": "https://json.schemastore.org/sarif-2.1.0.json",
"runs": [
{
"columnKind": "utf16CodeUnits",
"originalUriBaseIds": {
"ROOTPATH": {
"uri": "file:///"
}
},
"properties": {
"megalinter": {
"docUrl": "https://megalinter.io/alpha/descriptors/repository_trivy",
"linterKey": "REPOSITORY_TRIVY",
"linterVersion": "0.43.1"
}
},
"results": [],
"tool": {
"driver": {
"fullName": "Trivy Vulnerability Scanner",
"informationUri": "https://github.com/aquasecurity/trivy",
"name": "Trivy (MegaLinter REPOSITORY_TRIVY)",
"rules": [],
"version": "0.43.1"
}
}
}
],
"version": "2.1.0"
}
}
This message is sent once by analysis, when a MegaLinter analysis is completed.
CodeTotal stops listening to the requestId pubsub channel once this message is received, as no new message will be sent through this channel.
Example of megalinterComplete messageType:
{
"messageType": "megalinterComplete",
"megaLinterStatus": "completed",
"requestId": "RQ_cbdd1d82-1e7b-11ee-9258-0242ac120004"
}
In some cases, it is not possible for MegaLinter to start an analysis because there has been an error during initialization.
In that case, CodeTotal stops listening to the pubsub channel, as no more messages will be sent.
List of errors:
missingAnalysisType
: Missing type (snippet, file or repository)gitCloneError
: Unable to clone the repository (probably not existing or not reachable)uploadedFileNotFound
: Unable to find file(s) that were supposed to be uploaded by a previous HTTP call to/upload-file
snippetGuessError
: Unable to automatically detect the language of a code snippetsnippetBuildError
: Unable to find a file extension for the guessed snipper language
Example of serverError messageType
{
"messageType": "serverError",
"message": "Unable to clone repository\nCmd('git') failed due to: exit code(128)\n cmdline: git clone -v -- https://github.com/nvuillam/node-sarif-builder2 /tmp/ct-megalinter-x0afpmcmb\n stderr: 'Cloning into '/tmp/ct-megalinter-x0afpmcmb'...\nfatal: could not read Username for 'https://github.com': No such device or address\n'",
"errorCode": "gitCloneError",
"errorDetails": {
"error": "Cmd('git') failed due to: exit code(128)\n cmdline: git clone -v -- https://github.com/nvuillam/node-sarif-builder2 /tmp/ct-megalinter-x0afpmcmb\n stderr: 'Cloning into '/tmp/ct-megalinter-x0afpmcmb'...\nfatal: could not read Username for 'https://github.com': No such device or address\n'"
},
"requestId": "RQ_e630c962-1e7c-11ee-9258-0242ac120004"
}
-
Create a sub-branch
main
branch, name it release/vX.XX.XX (example:release/v1.2.0
) -
Update CHANGELOG.md to add the section about the content of the new release. Leave empty the beta section, then commit.
-
Run
npm version --no-git-tag-version --new-version vX.XX.XX
(example:npm version --no-git-tag-version --new-version v1.2.0
) -
Commit updates on package.json and package-lock.json with message
release vX.XX.XX
-
Make a Pull Request to
main
. IMPORTANT: The PR title must containrelease
in lowercase. Example:New release v1.2.0
. -
Merge the PR if everything is ok (use Squash & Merge option), else perform linter fixes until jobs are green, then merge.
-
Create a new GitHub Release -> https://github.com/oxsecurity/codetotal/releases/new
- Input your new version tag string (ex:
v1.2.0
) - Name the release
CodeTotal vX.XX.XX
(ex:CodeTotal v1.2.0
) - Click on Generate Release Notes button
- Arrange the result with content of CHANGELOG.md if necessary (like breaking change instructions)
- Create the GitHub release: An automated job will be triggered and will release the associated CodeTotal Docker Image
- Input your new version tag string (ex: