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

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:

FilenamePredicateContents
.ruby-versionequals2.6.0
Gemfile.lockcontainsruby 2.6.0

Verify Ruby library

Like verifying the Ruby version, you can look at Gemfile.lock to verify a particular library is installed.

FilenamePredicateContents
Gemfile.lockcontainsrails (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:

FilenamePredicateContents
config/initializers/filter_parameter_logging.rbcontainsconfig.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.

FilenamePredicateContents
.python-versionequals3.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.

FilenamePredicateContents
requirements.txtcontainsDjango==2.1.7

Verify Java library

If you use Apache Ivy, you can easily validate the presence of a given Java library.

FilenamePredicateContents
ivy.xmlcontains<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.

FilenamePredicateContents
.circleci/config.ymlcontainsbundle exec rspec
.circleci/config.ymlcontains-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.

FilenamePredicateContents
deployment.yamlcontainsapiVersion: 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.

Version Constraint Check

The following table defines the most common version comparators you can use in conjunction with our Repo File Checks.

ComparatorDescription
=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.
xAn 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 ConstraintAccepted VersionsUnaccepted Versions
<1.2.31.2.2, 1.1.1, 0.4.01.2.3, 1.3.0, 2.0.0
~1.2.31.2.3, 1.2.4, 1.2.421.2.2, 1.3.0, 2.0.0
^1.2.31.2.0, 1.2.4, 1.9.11.1.1, 0.9.0, 2.0.0
~>1.21.2.0, 1.2.1, 1.2.421.1.1, 1.3.0, 2.0.0
1.2.x1.2.0, 1.2.1, 1.2.421.1.0, 1.3.0, 2.0.0
1.2.x || 1.4.x1.2.0, 1.4.01.1.0, 1.3.0, 2.0.0
1.2.3 - 1.3.31.2.3, 1.3.0, 1.3.31.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].