As software projects expand in size and complexity, maintaining consistent code standards becomes crucial. These code standards enhance the security, readability, extensibility, and maintainability of software projects. Building on a previous post, we will demonstrate how to use the SASjs library in conjunction with GitHub actions to ensure consistent SAS code quality throughout a repository.
Why enforce coding standards?
- Security: Consistent coding practices help prevent vulnerabilities and ensure that security measures are uniformly applied across the codebase.
- Readability: Improved readability makes it easier for developers to understand and review each other’s code, facilitating effective collaboration and onboarding new team members.
- Extensibility: Standardized code allows developers to add new features or modify existing ones with minimal disruption, ensuring the codebase can evolve smoothly over time.
- Maintainability: Consistent coding practices reduce code complexity, making it easier to debug, update, and refactor, ultimately leading to a more stable and reliable software product.
Checking for SAS Code Standard Violations with a SASjs Lint
To begin using SASjs to lint our code, we need to install it. SASjs is available through the Node Package Manager (NPM). Follow these steps for installation:
-
- Install NodeJS
- Navigate to your project in a CLI and run sasjs lint
Configuring SASjs to use your Code Standards
To configure SASjs to lint with your rules, you must set up a .sasjs file in your project’s home directory. The options available are listed in Table 1.
Option Name | Type | Default | Description |
noGremlins | Boolean | True | Don’t allow invisible characters |
allowedGremlins | List of strings | None | Hex codes representing allowed gremlins |
defaultHeader | String | None | A default header to be added to files when sasjs lint fix is run |
hasDoxygenHeader | Boolean | True | Identifies where a doxygen header does not begin in line 1 |
hasMacroNameInMend | Boolean | True | Checks if the macro name is in the %mend statement |
hasMacroParentheses | Boolean | True | Checks if there are spaces between the macro name and the opening parentheses |
hasRequiredMacroOptions | Boolean | False | Checks if macros have the options listed in requiredMacroOptions |
requiredMacroOptions | List of Strings | None | Macro options to enforce with hasRequiredMacroOptions |
ignoreList | List of Strings | None | List of files or directories to ignore |
indentationMultiple | Integer | 2 | Check to ensure the number of leading spaces is divided evenly by this number |
lineEndings | String | “off” | Check the line endings to ensure files are configured correctly. Options (lf, crlf, off) |
lowerCaseFileNames | Boolean | True | Checks that file names are always lowercase |
maxDataLineLength | Integer | 80 | Maximum line length for Data lines. Overrides maxLineLength |
maxHeaderLineLength | Integer | 80 | Maximum line length for the program header. Overrides maxLineLength |
maxLineLength | Integer | 80 | Maximum line length. |
noEncodedPasswords | Boolean | True | Ensures no rows contain a sas00X or sasenc type password |
noNestedMacros | Boolean | True | Checks if macros are defined inside another macro |
noSpacesInFileNames | Boolean | True | Checks if any file names have spaces in them |
noTabs | Boolean | True | Makes sure no tabs are used |
noTrailingSpaces | Boolean | True | Checks for lines with trailing spaces |
severityLevel | Dict | Allows the user to set the severity level to “warn” or “error” for each option |
Table 1: List of linting options provided by SASJS
An example .sasjs configuration for the default values is displayed here:
{ "hasDoxygenHeader": true, "hasMacroNameInMend": true, "hasMacroParentheses": true, "ignoreList": ["sasjsbuild/", "sasjsresults/"], "indentationMultiple": 2, "lineEndings": "off", "lowerCaseFileNames": true, "maxDataLineLength": 80, "maxHeaderLineLength": 80, "maxLineLength": 80, "noEncodedPasswords": true, "noNestedMacros": true, "noGremlins": true, "noSpacesInFileNames": true, "noTabs": true, "noTrailingSpaces": true, "defaultHeader": "/** Default File Header Here **/", "severityLevel": { "hasDoxygenHeader": "warn", "maxLineLength": "warn", "noTrailingSpaces": "error" } } |
Using GitHub Actions to Ensure Code Standards in a Protected Branch
Now we are ready to set up checks in GitHub. This will ensure the code that throws a linting error based on our configuration does not get merged into the main branch.
- In the .github/workflows directory, create a file sasjs-lint.yaml
- Copy the following into your sasjs-lint.yaml file if you are using the default git hub run environments,:
• on: • pull_request: • branches: • - main • - release* • • jobs: • build: • runs-on: ubuntu-latest • • strategy: • matrix: • node-version: [lts/*] • • steps: • - uses: actions/checkout@v2 • - name: Use Node.js ${{ matrix.node-version }} • uses: actions/setup-node@v2 • with: • node-version: ${{ matrix.node-version }} • cache: npm • - name: Check Code Style • run: npm run lint
- If you are using a custom build that already contains sasjs/cli you can use the following:
4. name: sasjs-lint 5. on: 6. pull_request: 7. branches: 8. - main 9. - release* 10. 11. jobs: 12. sasjs-lint: 13. runs-on: custom-build 14. 15. container: 16. image: custom-image 17. options: --user root 18. 19. steps: 20. - uses: actions/checkout@v2 21. - name: Run the lint (code quality) check 22. run: sasjs lint
For any pull request targeting the main branch or a release branch, if a setting with a severity level marked as “error” is triggered, the workflow will fail and block the process. This will prevent merging any code that does not match the designated standards.
Summary
Using SASjs, we can customize our SAS code standards. We can then leverage GitHub Actions to ensure that any code merged into our protected branches meets these standards. This will allow us to improve the security, readability, extensibility, and maintainability of our code.