Repo File Checks
Verify that the contents of a file in a service's repo satisfy some criteria, such as containing a certain string or JSON path.
After setting up a Git Repository Integration, OpsLevel can continuously scan your code repositories and verify all of the operational best practices you’ve defined.
Along with the integration comes two checks: the Repo File Check and the Repo Search Check. This article describes the former, as well as some of the cool things you can do with it.
The Repo File Check
With the Repo File Check, you can verify the existence or contents of a file in your repo. Repo File Checks by default will run from the relative path set when attaching a repository. You can specify a subdirectory from that relative path, or enable the absolute root path option to search from the root of the repository.
Curious about how you can use Repo File Checks within your organization? Well, let’s see some examples of different kinds of checks you can build:
Repo File Check Examples
Verify Ruby version
You can verify that a repo is using a given Ruby version using either .ruby-version
or Gemfile.lock
. For example:
Filename | Predicate | Contents |
---|---|---|
.ruby-version | equals | 2.6.0 |
Gemfile.lock | contains | ruby 2.6.0 |
Verify Ruby library
Like verifying the Ruby version, you can look at Gemfile.lock
to verify a particular library is installed.
Filename | Predicate | Contents |
---|---|---|
Gemfile.lock | contains | rails (5.2.0) |
Verify Rails is not logging passwords
Part of the Rails Security Guide talks about how to set up Rails to not log passwords. You can verify that your apps have this enabled:
Filename | Predicate | Contents |
---|---|---|
config/initializers/filter_parameter_logging.rb | contains | config.filter_parameters << :password |
Verify Python version
Much like verifying the Ruby version, if you use Pyenv, you can verify the version of Python with .python-version
.
Filename | Predicate | Contents |
---|---|---|
.python-version | equals | 3.7.0 |
Verify Python Library
If you use pip
and requirements.txt
, you can validate that a given Python library version is used. Be sure to freeze your requirements beforehand with pip freeze > requirements.txt
.
Filename | Predicate | Contents |
---|---|---|
requirements.txt | contains | Django==2.1.7 |
Verify Java library
If you use Apache Ivy, you can easily validate the presence of a given Java library.
Filename | Predicate | Contents |
---|---|---|
ivy.xml | contains | <dependency org="apache" name="command-lang" revision="2.0" |
FilenamePredicateContentsivy.xmlcontains<dependency org="apache" name="command-lang" revision="2.0"
Verify README.md exists
It’s good practice that every repo should have a README. Just create a check that validates that this file exists, without looking into its contents.
Verify CircleCI is setup properly
If you use CircleCI, you can verify any aspect of your continuous integration. For example, you may want to verify that linting is enabled with Danger or that you’re running tests with Rspec.
Filename | Predicate | Contents |
---|---|---|
.circleci/config.yml | contains | bundle exec rspec |
.circleci/config.yml | contains | -run:danger |
Verify a recent version of Kubernetes
Older versions of Kubernetes had an apiVersion
of apps/v1beta2
. You can verify that your repos are all using the latest version of Kubernetes in their deployments.
Filename | Predicate | Contents |
---|---|---|
deployment.yaml | contains | apiVersion: apps/v1 |
Using a jq predicate (advanced)
With jq checks you will be able to create specific checks against the contents of JSON and YAML files in your Repositories.
A jq predicate check will pass if the contents of your file returns a truthy value when evaluated against the provided jq expression. Currently jq predicate checks are supported on JSON and YAML files.
Example: Replica Count Check
Check for an odd number of replicas, greater than 2, in a kubernetes manifest file.
Example file: application/zookeeper/zookeeper.yaml
kind: StatefulSet
metadata:
name: zk
spec:
selector:
matchLabels:
app: zk
serviceName: zk-hs
replicas: 5
The jq expression: .spec.replicas % 2 == 1 and .spec.replicas > 2
will evaluate to true, meaning this file will pass the check. If the replicas
value gets updated to 1 or any even number, the file will no longer pass the check.
Example: Version Check
Check for a specific version of jenkins
dependencies:
- name: jenkins
repository: https://storage.example.com
version: 1.3.6
The jq expression: .dependencies\[\] | select(.name == "jenkins" and .version == "1.3.6")
will return an object if there is a dependency on Jenkins version 1.3.6. This means the file will pass the check, since an object
is considered to be a truthy
value.
Testing your jq predicate
jq checks can take a few tries before they are setup correctly. If you are trying to troubleshoot a check, it might be easier to test it locally than in the app, depending on your setup. For this, you will need to install jq, which is available on most popular package managers.
For example where {$filter_expression}
is what you will be using as your check, and {$file_path}
being the file in question. jq
can be used like:
jq ${filter_expression} ${file_path}
Alternatively there is also jq play if you don’t want to install anything locally. However be careful and redact any potentially sensitive data before pasting it into third party sites.
If your files are in YAML format, then you can use yq, a YAML wrapper around jq
. Unfortunately its a bit limited, so you will need to pipe to jq
to get the expression to evaluate like it will in the check:
yq r -j "${file_path}" | jq "${filter_expression}"
To make this a bit easier, you can add this to wherever you keep your shell scripts:
function yjq() {
yq r -j $2 | jq $1
}
And then you will be able to use yjq
, like it were jq
against YAML files on your machine.
Using a version constraint (advanced)
Check if a file contains a matching semantic version. Please note this only works with files like .node-version
or .ruby-version
that just contain the semantic version. It does not work with lock files such as Gemfile.lock
or package.json
or pom.xml
Example
The following check ensures that the service’s repository has a file named .node-version
with a version that is at least 12.0.0
.
The following table defines the most common version comparators you can use in conjunction with our Repo File Checks.
Comparator | Description |
---|---|
= | The “equals” comparator can be used to ensure that your library version matches an expected version exactly (that is, the major, minor and patch are all equal). This operator is, however, optional. A constraint without any operator will default to using the “equals” comparator. |
< | The “less than” comparator can be used to ensure that your library version is less than an expected version (with order of precedence being (1) major (2) minor and (3) patch). |
<= | The “less than or equal to” comparator is similar to the < comparator, except an exact match against an expected version is also allowed. |
> | The “greater than” comparator is analagous to the < comparator, except the library version must be greater than the expected version. |
>= | The “greater than or equal to” comparator is analagous to the <= comparator, except the library version must be greater than or equal to the expected version. |
~ | The “tilde” comparator ensures that the library version matches the major and minor versions of an expected version, but can have an equal or greater patch version. |
^ | The “caret” comparator can be used to ensure that the library version matches the major version of an expected version, but can have an equal or greater minor version. |
~> | The “pessimistic” comparator can be used to ensure that a library version matches the expected version up to the last component specified. See this Ruby Gems guide for a complete description. |
x | An x in any position of an expected version behaves like a wildcard. That is, any version can be substituted for the component containing the x . |
The following table shows some examples of version constraints along with examples of versions that would and wouldn’t be accepted.
Version Constraint | Accepted Versions | Unaccepted Versions |
---|---|---|
<1.2.3 | 1.2.2 , 1.1.1 , 0.4.0 | 1.2.3 , 1.3.0 , 2.0.0 |
~1.2.3 | 1.2.3 , 1.2.4 , 1.2.42 | 1.2.2 , 1.3.0 , 2.0.0 |
^1.2.3 | 1.2.0 , 1.2.4 , 1.9.1 | 1.1.1 , 0.9.0 , 2.0.0 |
~>1.2 | 1.2.0 , 1.2.1 , 1.2.42 | 1.1.1 , 1.3.0 , 2.0.0 |
1.2.x | 1.2.0 , 1.2.1 , 1.2.42 | 1.1.0 , 1.3.0 , 2.0.0 |
1.2.x || 1.4.x | 1.2.0 , 1.4.0 | 1.1.0 , 1.3.0 , 2.0.0 |
1.2.3 - 1.3.3 | 1.2.3 , 1.3.0 , 1.3.3 | 1.2.2 , 1.3.4 |
See the node-semver guides for an exhaustive list of comparators you can use to build advanced version constraints.
And more!
Ok, those were just some examples to get the creative juices flowing. But there’s tons more you can do with Repo file checks. If you have any questions, or just come up with some interesting checks you want to share, hit us up at [email protected].
Updated 8 months ago