github.com/google/osv-scalibr@v0.4.1/docs/style_guide.md (about) 1 # OSV-SCALIBR style guide 2 3 OSV-SCALIBR, like most of Google's Go projects, follows the [Google Go style 4 guide](https://google.github.io/styleguide/go). Apart from this, we have some 5 specific OSV-SCALIBR specific best practices: 6 7 ## Code structure 8 9 ### Line length 10 11 Use 80 characters per line when possible. Exceptions apply if splitting 12 something into several lines hurts readability. 13 14 ### Short functions 15 16 Prefer short functions. If a function is getting too large, consider moving 17 self-contained parts of the business logic into separate private helper 18 functions with descriptive names, even if they're only called once. 19 20 Similarly, if the list of params to a function is getting too long, consider 21 moving them to an [option 22 structure](https://google.github.io/styleguide/go/best-practices.html#option-structure). 23 This shortens the function definitions and makes it easier to oversee the 24 purpose of each param. 25 26 ### Function ordering: General to specific 27 28 Define the public structs/functions/etc. in a class first, then define the 29 private functions. 30 31 Within private functions, define the higher-order ones first. E.g. if function 32 `a()` is calling `b()`, define `a` first, then `b`. 33 34 ### Short inline functions 35 36 Keep inline functions short. If they're getting long, prefer moving them into a 37 separate named private function. 38 39 Since inline functions can capture vars from the parent function they can get 40 hard to oversee if they grow too large. By factoring them into separate 41 functions, any vars from the parent function have to be passed as parameters, 42 making it easier to understand how the function affects the surrounding code. 43 44 ### Inline constants 45 46 If a constant is private and used only once, prefer inlining it where it's used 47 instead of adding a top-level const declaration. 48 49 Regexes are exempt from this since they should be initialized at startup - See 50 the section below for more details. 51 52 ### Avoid init() 53 54 Usage of `init()` makes it hard to keep track of the control flow. Prefer 55 avoiding it in production code. In tests you can use `TestMain()` instead. 56 57 ### Context propagation 58 59 OSV-SCALIBR library users can pass in a `context.Context` to control timeouts 60 and context cancellation. Make sure this top-level context is passed down to 61 lower functions (don't initialize a new context with `context.Background()`) and 62 check for context cancellation whenever something long-running is performed such 63 as looping 64 ([example](https://github.com/google/osv-scalibr/blob/8b03d0859edf445152f34c420f50ffe0abf057df/extractor/filesystem/os/dpkg/dpkg.go#L183)). 65 66 ## Error handling 67 68 ### Avoid panics 69 70 If a plugin encounters an error the rest of OSV-SCALIBR and the callers' code 71 shouldn't crash. Avoid calling panics and prefer propagating errors instead. 72 73 ### Init regexes at startup time 74 75 Add all regex definitions that use `MustCompile` as global vars that initialize 76 at startup time 77 ([example](https://github.com/google/osv-scalibr/blob/8b03d0859edf445152f34c420f50ffe0abf057df/extractor/filesystem/os/nix/nix.go#L92)). 78 This allows the initialization computation to be done up front and catches any 79 potential crashes before the scan runs. 80 81 ### Propagate or log errors 82 83 In general, propagate errors upwards to the caller. 84 85 If the error is expected or not something that should make the module fail (e.g. 86 an Extractor encountered an invalid package.json file) there's no need to 87 propagate it but consider logging a warning instead. 88 89 ## Testing 90 91 ### Don't use `t.Parallel()` 92 93 While `t.Parallel()` allows tests to run faster, they cause test logs in our 94 internal systems to be mixed together for various test cases, making them harder 95 to read. OSV-SCALIBR unit tests also only take a couple of seconds to run so 96 there's not much benefit in adding `t.Parallel()` at the moment. 97 98 ### Avoid assertion libraries 99 100 Generally avoid creating helper libraries that [perform test 101 assertions](https://google.github.io/styleguide/go/decisions.html#assertion-libraries). 102 Instead, use helper libs to transform your data into a more easily comparable 103 structure and perform the comparisions/assertions in the main test function. 104 Example: the 105 [extracttest](https://github.com/google/osv-scalibr/blob/8b03d0859edf445152f34c420f50ffe0abf057df/extractor/filesystem/language/dart/pubspec/pubspec_test.go#L296) 106 helper lib. 107 108 An exception is when the helper library is used to set up the testing 109 environment (e.g. create specific files). In these cases it's fine to assert 110 that the setup succeeded in the library function as long as the setup code is 111 not related to the functionality being tested 112 ([example](https://github.com/google/osv-scalibr/blob/8b03d0859edf445152f34c420f50ffe0abf057df/extractor/filesystem/os/dpkg/dpkg_test.go#L1527)). 113 114 ### Use easy to find subtest descriptions 115 116 Use only alphanumeric characters and underscores in test descriptions. Don't use 117 spaces. Test logs transform these descriptions by substituting the spaces which 118 makes the failing tests from the logs harder to find in the code 119 ([example](/binary/cli/cli_test.go#L258;rcl=732940634)). 120 121 ### Test for multi-platform support 122 123 OSV-SCALIBR runs on Linux, Windows, and Mac. When adding new code, make sure 124 your code is compatible with all 3 OSes or that you're adding a component that's 125 only meant to run on a given OS. Check that the SCALIBR Github Actions for all 3 126 OSes pass. 127 128 When using OS specific helper libraries consider adding dummy implementations 129 for other OSes 130 ([example](https://github.com/google/osv-scalibr/blob/main/extractor/standalone/windows/ospackages/ospackages_dummy.go)). 131 132 One common change that fails on Window is introducing file path processing code 133 that uses the wrong kinds of slashes (`/` vs `\`). When dealing with absolute 134 paths, use built-in functions such as `filepath.Join()` to handle path 135 operations. Virtual paths use the `fs.FS` interface which uses `/` even on 136 Windows. In these cases you can sanitize your paths with `filepath.ToSlash` 137 ([example](https://github.com/google/osv-scalibr/blob/daa1498e42aafe6a9258df854cb3bfee17b6808b/extractor/filesystem/language/python/requirements/requirements.go#L193)). 138 139 ## Performance 140 141 OSV-SCALIBR is meant to also run on systems with constrained resources and new 142 code should thus try to keep its runtime and resource usage low. Plugins that 143 have a high resource consumption will be able to run in less contexts and will 144 thus be less useful. 145 146 ### Avoid expensive operations in `FileRequired` 147 148 Extractor plugins' `FileRequired()` function can get called on every file on the 149 scanned filesystem. Keep the checks simple by using simple string comparison 150 logic. Define the checks inside `FileRequired()` instead of separate functions 151 as function calls can add additional runtime overhead. 152 153 Avoid doing expensive file path comparisons such as regexp matching unless 154 you've already pre-filtered the files and can be sure that the more expensive 155 operations will only run on a small subset of the files. 156 157 ### Avoid reading full binaries into memory 158 159 When parsing binaries and lockfiles that can get large, avoid reading all of the 160 file contents into memory whenever possible. Prefer to use streaming readers. 161 For reading a specific section of a large file, prefer using 162 [`ReadAt()`](https://pkg.go.dev/io#ReaderAt) instead of slicing out the relevant 163 sections in memory. 164 165 ## Miscellaneous 166 167 ### Use the Unit lib for large numbers 168 169 OSV-SCALIBR has a 170 [unit lib](https://github.com/google/osv-scalibr/blob/main/extractor/filesystem/internal/units/units.go) 171 for commonly used data size units. Use the values from these lib instead code 172 like `"2 * 1024 * 1024"`. 173 174 ### Prefer %q over %s 175 176 When formatting strings, `%q` adds escapes and quotation marks and makes it 177 easier to see where a string variable in the log message starts. It also makes 178 it easier to see empty strings in logs 179 ([example](https://github.com/google/osv-scalibr/blob/daa1498e42aafe6a9258df854cb3bfee17b6808b/scalibr.go#L118)). 180 181 ### Add docstrings to public functions and types 182 183 All public functions and type should have [doc 184 comments](https://tip.golang.org/doc/comment).