About¶
Below are a list of ways that you can help contribute to this project, as well as policies and guides that explain how to get started.
Please review everything on this page before you submit your contribution.
- About
- Issues, Bugs, Ideas
- Contributing
- Before Submitting Pull Requests
- Conventional Commit Specification
- Committing
- Languages
Issues, Bugs, Ideas¶
Stuff happens, and sometimes as best as we try, there may be issues within this project that we are unaware of. That is the great thing about open-source; anyone can use the program and contribute to making it better.
If you have found a bug, have an issue, or maybe even a cool idea; you can let us know by submitting it. However, before you submit your new issue, bug report, or feature request; head over to the Issues Section and ensure nobody else has already submitted it.
Once you are sure that your issue has not already being dealt with; you may submit a new issue at here. You'll be asked to specify exactly what your new submission targets, such as: - Bug report - Feature Suggestion
When writing a new submission; ensure you fill out any of the questions asked of you. If you do not provide enough information, we cannot help. Be as detailed as possible, and provide any logs or screenshots you may have to help us better understand what you mean. Failure to fill out the submission properly may result in it being closed without a response.
If you are submitting a bug report:
- Explain the issue
- Describe how you expect for a feature to work, and what you're seeing instead of what you expected.
- List possible options for a resolution or insight
- Provide screenshots, logs, or anything else that can visually help track down the issue.
Contributing¶
If you are looking to contribute to this project by actually submit your own code; please review this section completely. There is important information and policies provided below that you must follow for your pull request to get accepted.
The source is here for everyone to collectively share and collaborate on. If you think you have a possible solution to a problem; don't be afraid to get your hands dirty.
All contributions are made via pull requests. To create a pull request, you need a GitHub account. If you are unclear on this process, see GitHub's documentation on forking and pull requests. Pull requests should be targeted at the master branch.
Before Submitting Pull Requests¶
- Follow the repository's code formatting conventions (see below);
- Include tests that prove that the change works as intended and does not add regressions;
- Document the changes in the code and/or the project's documentation;
- Your PR must pass the CI pipeline;
- When submitting your Pull Request, use one of the following branches:
- For bug fixes:
main
branch - For features & functionality:
development
branch - Include a proper git commit message following the Conventional Commit Specification.
If you have completed the above tasks, the pull request is ready to be reviewed and your pull request's label will be changed to "Ready for Review". At this point, a human will need to step in and manually verify your submission.
Reviewers will approve the pull request once they are satisfied with the patch it will be merged.
Conventional Commit Specification¶
When committing your changes, we require you to follow the Conventional Commit Specification. The Conventional Commits is a specification for the format and content of a commit message. The concept behind Conventional Commits is to provide a rich commit history that can be read and understood by both humans and automated tools. Conventional Commits have the following format:
Types¶
Our repositories make use of the following commit tags:
Type | Description |
---|---|
feat |
Introduce new feature |
fix |
Bug fix |
chore |
Includes technical or preventative maintenance task that is necessary for managing the app or repo, such as updating grunt tasks, but is not tied to any specific feature. Usually done for maintenance purposes. E.g: Edit .gitignore, .prettierrc, .prettierignore, .gitignore, eslint.config.js file |
revert |
Revert a previous commit |
style |
Update / reformat style of source code. Does not change the way app is implemented. Changes that do not affect the meaning of the code E.g: white-space, formatting, missing semi-colons, change tabs to spaces, etc) |
docs |
Change website or markdown documents. Does not mean changes to the documentation generator script itself, only the documents created from the generator. E.g: documentation, readme.md or markdown |
build |
Changes to the build / compilation / packaging process or auxiliary tools such as doc generation E.g: create new build tasks, update release script, etc. |
refactor |
Change to production code that leads to no behavior difference, E.g: split files, rename variables, rename package, improve code style, etc. |
test |
Add or refactor tests, no production code change. Changes the suite of automated tests for the app. |
ci |
Changes related to Continuous Integration (usually yml and other configuration files). |
perf |
Performance improvement of algorithms or execution time of the app. Does not change an existing feature. |
Example 1:¶
feat(core): bug affecting menu [#22]
^───^────^ ^─────────────────^ ^───^
| | | |
| | | └─⫸ (ISSUE): Reference issue ID
│ │ │
│ │ └──────────────────────⫸ (DESC): Summary in present tense. Use lower case not title case!
│ │
│ └──────────────────────────────⫸ (SCOPE): The package(s) that this change affects
│
└──────────────────────────────────⫸ (TYPE): See list above
Example 2:¶
<type>(<scope>): <short summary> [issue]
| | | |
| | | └─⫸ Reference issue id (optional)
│ │ │
│ │ └─⫸ Summary in present tense. Not capitalized. No period at the end.
│ │
│ └─⫸ Commit Scope: animations|bazel|benchpress|common|compiler|compiler-cli|core|
│ elements|forms|http|language-service|localize|platform-browser|
│ platform-browser-dynamic|platform-server|router|service-worker|
│ upgrade|zone.js|packaging|changelog|docs-infra|migrations|ngcc|ve|
│ devtools....
│
└─⫸ Commit Type: build|ci|doc|docs|feat|fix|perf|refactor|test
website|chore|style|type|revert|deprecate
Committing¶
If you are pushing a commit which addresses a submitted issue, reference your issue at the end of the commit message. You may also optionally add the major issue to the end of your commit body.
References should be on their own line, following the word Ref
or Refs
Title: fix(core): fix error message displayed to users. [#22]
Description: The description of your commit
Ref: #22, #34, #37
Languages¶
The formatting of code greatly depends on the language being used for this repository. We provide various different languages below as this guide is utilized across multiple repositories.
Perl¶
The following guidelines apply to any projects written with Perl:
Indentation¶
Use 4 spaces
per indentation level.
if (scalar(keys %versions) == 0)
{
if ($DEBUG)
{
dbg("=== csget: No version files to fetch — exiting ===\n");
}
my $status_file = "/var/lib/configserver/last_run_no_versions";
if (!-d "/var/lib/configserver")
{
system("mkdir -p /var/lib/configserver") == 0
or die "Failed to create /var/lib/configserver for status file";
}
system("touch $status_file") == 0
or warn "Failed to create status file $status_file";
exit 0;
}
if (scalar(keys %versions) == 0)
{
if ($DEBUG)
{
dbg("=== csget: No version files to fetch — exiting ===\n");
}
my $status_file = "/var/lib/configserver/last_run_no_versions";
if (!-d "/var/lib/configserver")
{
system("mkdir -p /var/lib/configserver") == 0
or die "Failed to create /var/lib/configserver for status file";
}
system("touch $status_file") == 0
or warn "Failed to create status file $status_file";
exit 0;
}
Line Length¶
Keep the maximum character count to 100 characters per line
. If you are revising old code which doesn't follow this guideline; please rewrite it to conform.
Blank Lines¶
Surround top-level functions and class definitions with a blank line in-between.
Method definitions inside a class are surrounded by a single blank line.
Extra blank lines may be used (sparingly) to separate groups of functions related to one another. Blank lines may be omitted between a bunch of related one-liners (e.g: set of dummy implementations).
# Top-level functions separated by blank lines
sub fetch_versions
{
# fetch versions logic
dbg("Fetching versions...\n");
}
sub check_versions
{
# check versions logic
dbg("Checking versions...\n");
}
# Methods inside a package/class with single blank lines in-between
package MyClass;
sub new
{
my ($class) = @_;
bless {}, $class;
}
sub run
{
dbg("Running...\n");
}
sub stop
{
dbg("Stopping...\n");
}
# Top-level functions crammed together
sub fetch_versions
{
dbg("Fetching versions...\n");
}
sub check_versions
{
dbg("Checking versions...\n");
}
# Methods inside a package/class with no spacing
package MyClass;
sub new
{
my ($class) = @_;
bless {}, $class;
}
sub run
{
dbg("Running...\n");
}
sub stop
{
dbg("Stopping...\n");
}
Imports¶
When importing modules using use
and require
, try to observe the following:
- No namespace polluting, only include what you need
- Use
require
for modules that are needed conditionally at runtime. - Prefer
use
for compile-time imports when possible, often safter and catches errors early. - Keep imports at the top of the file for clarity.
- Use fully qualified names when you don’t need to import symbols.
- Group related imports together and separate core, CPAN, and local modules for readability.
- Avoid importing entire modules with use Module; unless you truly need everything.
- Document any unusual or non-standard imports so readers understand why they are used.
# Only import what is needed, keep imports organized
use strict;
use warnings;
use File::Path qw(make_path); # Only import make_path function
use List::Util qw(sum max); # Only import specific functions
# Local module import with fully qualified usage
use My::Utils; # We'll call functions as My::Utils::function()
Commenting¶
Comment your code. It helps novice readers to better understand the process. It doesn't have to be painfully obvious explanations, but it helps to give an idea of what something does. Explanations should be quick and to the point.
Do not include comments above functions that basically say the name of the function all over again.
Please append #
to the beginning of each line.
# #
# Daemonize / fork the script when not in debug mode
# sudo perl -w /etc/cron.daily/csget --nosleep
# sudo perl -d /etc/cron.daily/csget
#
# - Forks the process: parent exits, freeing the terminal or cron
# - Child process continues running in the background
# - Changes working directory to root to avoid locking any directory
# - Closes standard filehandles (STDIN, STDOUT, STDERR) for background operation
# - Redirects STDIN from /dev/null
# - Redirects STDOUT and STDERR to the daemon log file
# - Ensures that all output/errors from the daemon are captured in the log
# #
unless ($DEBUG)
{
if (my $pid = fork) { exit 0; } # parent
elsif (defined($pid)) { $pid = $$; } # child
else { die "Unable to fork: $!"; } # cannot fork
chdir("/");
close(STDIN);
close(STDOUT);
close(STDERR);
open(STDIN, "<", "/dev/null");
open(STDOUT, ">>", "$log_daemon")
or die "Cannot open STDOUT log: $!";
open(STDERR, ">>", "$log_daemon")
or die "Cannot open STDERR log: $!";
}
# #
# does not run in debug mode
# #
unless ($DEBUG)
{
if (my $pid = fork) { exit 0; }
elsif (defined($pid)) { $pid = $$; }
else { die "Unable to fork: $!"; }
chdir("/");
close(STDIN);
close(STDOUT);
close(STDERR);
open(STDIN, "<", "/dev/null");
open(STDOUT, ">>", "$log_daemon")
or die "Cannot open STDOUT log: $!";
open(STDERR, ">>", "$log_daemon")
or die "Cannot open STDERR log: $!";
}
Just repeating the information over again:
Casing¶
- Use
camelCase
for variable and object names- e.g: userName, totalCount
- Functions should start with an uppercase letter (PascalCase) if following project convention
- e.g: CalculateSum()
- Enums and constants should be capitalized or use ALL_CAPS
- e.g: STATUS_ACTIVE, ColorRed
- When reviewing code, if you encounter names that do not follow this convention, update them in your pull request to maintain consistency.
Python¶
The following guidelines apply to any projects written with Python:
Indentation¶
Use 4 spaces
per indentation level.
Line Length¶
Keep the maximum character count to 100 characters per line
. If you are revising old code which doesn't follow this guideline; please rewrite it to conform.
import requests
# Long URL split across multiple lines using parentheses
def fetch_user_data(user_id: str) -> dict:
response = requests.get(
f"https://api.example.com/users/{user_id}/details?"
f"include=posts,comments,likes,shares"
)
return response.json()
# Using backslash for line continuation (less preferred, usually for long strings)
def build_command() -> str:
long_command = "python script.py --option1 value1 --option2 value2 --option3 value3 " \
"--option4 value4 --option5 value5"
return long_command
import requests
# URL is way too long on a single line, exceeds 100 characters
def fetch_user_data(user_id: str) -> dict:
response = requests.get(f"https://api.example.com/users/{user_id}/details?include=posts,comments,likes,shares")
return response.json()
# Long shell command all in one line, very hard to read
def build_command() -> str:
long_command = "python script.py --option1 value1 --option2 value2 --option3 value3 --option4 value4 --option5 value5"
return long_command
Blank Lines¶
Surround top-level functions and class definitions with a blank line in-between.
Method definitions inside a class are surrounded by a single blank line.
Extra blank lines may be used (sparingly) to separate groups of functions related to one another. Blank lines may be omitted between a bunch of related one-liners (e.g: set of dummy implementations).
# #
# Top-level function with a blank line before and after
# #
def initialize_server():
print("Server initialized")
def shutdown_server():
print("Server shutdown")
class ServerManager:
# #
# Method inside a class separated by a single blank line#
# #
def start(self):
print("Starting server")
def stop(self):
print("Stopping server")
Imports¶
Imports should usually be on separate lines:
Commenting¶
Please follow these guidelines for commenting:
- Use comments to explain why something is done, not what is obvious from the code.
- Keep comments concise and clear.
- Do not repeat the function name or code in the comment.
- Block comments should be used to give a brief explanation of something to note as a developer.
- Docstrings should be used when writing important descriptions, arguments usage, etc.
- Wrap block comments in
#
, two on top, two on bottom, one per comment line.
# #
# Docstring Example
# #
def fetch_user_data(user_id: str) -> dict:
"""
Fetches user data from the API.
Args:
user_id (str): The unique identifier for the user.
Returns:
dict: JSON data containing user information such as
'name', 'email', 'posts', and 'comments'.
Raises:
requests.RequestException: If the API call fails.
"""
import requests
response = requests.get(f"https://api.example.com/users/{user_id}")
response.raise_for_status()
return response.json()
Casing¶
- Stick to
camelCase
; unless: - naming functions, capitalize the first letter
- Capitalize enums
- If you see code not conforming with this, please revise it in your pull request.
[!TIP] ✅ Correct
[!CAUTION] ❌ Wrong
NodeJS¶
The following allows you to configure ESLint and Prettier.
Prettier¶
We have opted to make use of ESLint over Prettier. We provide a detailed ESLint flag config file with very specific linting rules. Please review that section for more information.
ESLint¶
Within the root folder of the repo, there are several configuration files which you should be using within the project. These files dictate how prettier and eslint will behave and what is acceptable / not acceptable.
Pick the config file below depending on which version of ESLint you are using. The v8 and older .eslint
may not be there if we have migrated over to an Eslint v9 flat config file:
v9 & Newer (Config)¶
Our NodeJS applications require that you utilize ESLint v9 or newer which makes use of a flat config structure. You may find a copy of our flat config at the link below:
v8 & Older (Config)¶
- We no longer utilize any version of ESLint older than version 9.
Note
When submitting your pull request, these linting and style rules will be verified with all of your files. If you did not follow these rules; the linter tests on your pull request will fail; and you'll be expected to correct these issues before your submission will be transferred over for human review.
Packages¶
We use the following packages for linting and prettier.
Package | Repo File | Description |
---|---|---|
@stylistic/eslint-plugin-js | package.json | JavaScript stylistic rules for ESLint, migrated from eslint core. |
@stylistic/eslint-plugin-ts | package.json | TypeScript stylistic rules for ESLint, migrated from typescript-eslint. |
@stylistic/eslint-plugin-plus | package.json | Supplementary rules introduced by ESLint Stylistic. |
eslint-plugin-prettier | package.json | Runs Prettier as an ESLint rule and reports differences as individual ESLint issues. |
You can add the following to your package.json
file:
"devDependencies": {
"@types/uuid": "^10.0.0",
"all-contributors-cli": "^6.26.1",
"uuid": "^11.1.0",
"env-cmd": "^10.1.0",
"eslint": "9.17.0",
"eslint-plugin-chai-friendly": "^1.0.1",
"eslint-plugin-import": "2.31.0",
"eslint-plugin-n": "17.15.0",
"eslint-plugin-promise": "7.2.1",
"@stylistic/eslint-plugin-js": "^3.1.0"
},
Indentation¶
Use 4 spaces
per indentation level.
Style¶
For files that are not controlled by Prettier or ESLint; use Allman Style
. Braces should be on their own lines, and any code inside the braces should be indented 4 spaces.
return {
status: "failure",
user:
{
id: "1aaa35aa-fb3a-62ae-ffec-a14g7fc401ac",
label: "Test String",
}
};
while (x == y)
{
foo();
bar();
}
Line Length¶
Keep the maximum character count to 100 characters per line
. The configs on this page have prettier automatically set up to detect more than 100 characters per line.
Commenting¶
Comment your code. It helps novice readers to better understand the process. You may use block style commenting, or single lines:
/*
tests to decide if the end-user is running on Darwin or another platform.
*/
test(`Return true if platform is Darwin`, () =>
{
process.platform = 'darwin';
expect(bIsDarwin()).toBe(true);
});
test(`Return false if platform is not Darwin`, () =>
{
process.platform = 'linux';
expect(bIsDarwin()).toBe(false);
});
Casing¶
Stick to camelCase
as much as possible.
If you are defining a new environment variable; it must be in ALL CAPS in the Dockerfile
:
ENV DIR_BUILD=/usr/src/app
ENV DIR_RUN=/usr/bin/app
ENV URL_REPO="https://github.com/Aetherinox/csf-firewall"
ENV WEB_IP="0.0.0.0"
ENV WEB_PORT=4124
ENV LOG_LEVEL=4
ENV TZ="Etc/UTC"
Then you may call your new environment variable within the Javascript code; and ensure you define a default value to correct any user misconfigurations:
const envUrlRepo = process.env.URL_REPO || 'https://git.binaryninja.net/binaryninja';
const envStreamQuality = process.env.STREAM_QUALITY || 'hd';
const envFileM3U = process.env.FILE_PLAYLIST || 'playlist.m3u8';
const envFileXML = process.env.FILE_EPG || 'xmltv.xml';
const envFileTAR = process.env.FILE_TAR || 'xmltv.xml.gz';