Skip to content

Guardrails / Collectors

Ruby project ​

The Bundler project in the project directory: the Ruby version it asks for, the gem sources it resolves from, the gems each manifest declares with their groups and origins, and the lock file beside them. The Gemfile is Ruby rather than a declaration, so what it declares conditionally is not seen and scanned says so.

Facts keyruby
Versionv1
Scriptruby.py
Timeout30 seconds

Inputs ​

InputDescriptionDefaultEnvironment
projectDirDirectory holding the project, relative to the directory the CLI runs in..GUARDRAIL_INPUT_PROJECTDIR

When a guardrail declares an input with the same name, it passes its value through. That means you set these values in the guardrail's configuration in buildnote.json.

A guardrail asks for these facts by name and reads them back by the same name:

json
{
  "collect": ["ruby"]
}
python
ruby = guardrail.facts("ruby")

Facts ​

These are the fields of the document ruby collects. In a path, [] means each entry of the list before it, and [path] means a key of the object before it.

FactMeaning
directoryThe project directory set by the guardrail's projectDir input, relative to the directory the CLI runs in. Every other path this collector reports is also relative to the directory the CLI runs in, so it resolves from where you invoked the CLI, not from wherever the collector happened to run.
existsWhether that directory exists. When it doesn't, nothing else is collected.
manifestPath of the primary manifest, from the directory the CLI runs in: the Gemfile when there is one, otherwise the gemspec.
sourcesEvery manifest that was read, in the order they were read.
scannedEvery manifest that is Ruby rather than a declaration and was read by pattern. A gem added inside a condition, a loop or an eval is not seen, so dependencies read from one of these is a minimum, not the complete list.
declaredThe Ruby version the project asks for, or null when it asks for none. .ruby-version wins over the ruby directive, because both a developer's shell and the runner's version manager read it. The ruby directive in turn wins over a gemspec's required_ruby_version, which is the lowest version the gem supports, not the version this checkout runs.
declared.versionConstraint exactly as written, such as 3.3.1 or >= 3.2.
declared.sourceFile declaring it, from the directory the CLI runs in.
declared.pinnedWhether the constraint names one exact version rather than a range. A ~>, a >= or a * is not pinned.
bundlerVersion of Bundler Gemfile.lock records as the one that wrote it, or null when no lock file is committed.
sources_declaredEvery gem source the Gemfile names, such as https://rubygems.org. A second source is what a dependency confusion attack needs.
projectsThe one gem the checkout declares, or the project directory itself when it declares none. Bundler has no workspaces.
projects[].pathDirectory of the gem, from the directory the CLI runs in, always directory itself.
projects[].manifestIts gemspec, from the directory the CLI runs in, or the Gemfile when the checkout carries no gemspec.
projects[].nameName the gemspec declares, or null when there is none to read it from.
dependenciesWhat the manifests declare, split by whether the project asks for it itself.
dependencies.directEvery gem call the reader saw, in the order they appear.
dependencies.direct[].nameGem name, such as rails.
dependencies.direct[].versionRequirement exactly as written, such as ~> 7.1, or null when the call names none and any version will do.
dependencies.direct[].scopesThe Bundler groups the gem is in: default when the call is at the top level, otherwise the group it sits inside, such as development or test.
dependencies.direct[].sourceManifest declaring it, from the directory the CLI runs in.
dependencies.direct[].pinnedWhether the requirement names one exact version. A ~>, a >=, a git: or a path: option is not pinned.
dependencies.direct[].originWhere it comes from: registry, git when the call carries a git: or github: option, or path when it carries a path: option.
dependencies.transitiveAlways empty. What Gemfile.lock resolves is not read: lockfiles says whether one is committed.
lockfilesGemfile.lock when it is committed, from the directory the CLI runs in. An application without one resolves differently on every deploy.
unparsedEvery manifest the reader could not open, so a guardrail can tell a project that declares nothing apart from one that could not be read.
unparsed[].pathPath of that manifest, from the directory the CLI runs in.
unparsed[].reasonWhy the reader could not handle it.

If a collector can't finish, it prints what it gathered so far along with an incomplete key that says why. Facts after the point where it stopped are missing, so a check that depends on them should read incomplete first.

Example facts ​

Here are the facts the ruby collector gathers from an example project:

json
{
  "directory": ".",
  "exists": true,
  "manifest": "Gemfile",
  "sources": [
    "Gemfile"
  ],
  "scanned": [
    "Gemfile"
  ],
  "declared": {
    "version": "3.3.1",
    "source": "Gemfile",
    "pinned": true
  },
  "bundler": "2.5.9",
  "sources_declared": [
    "https://rubygems.org"
  ],
  "projects": [
    {
      "path": ".",
      "manifest": "Gemfile",
      "name": null
    }
  ],
  "dependencies": {
    "direct": [
      {
        "name": "rails",
        "version": "~> 7.1.3",
        "scopes": [
          "default"
        ],
        "source": "Gemfile",
        "pinned": false,
        "origin": "registry"
      },
      {
        "name": "pg",
        "version": "1.5.6",
        "scopes": [
          "default"
        ],
        "source": "Gemfile",
        "pinned": true,
        "origin": "registry"
      },
      {
        "name": "puma",
        "version": ">= 6.4",
        "scopes": [
          "default"
        ],
        "source": "Gemfile",
        "pinned": false,
        "origin": "registry"
      },
      {
        "name": "rspec-rails",
        "version": "~> 6.1",
        "scopes": [
          "development",
          "test"
        ],
        "source": "Gemfile",
        "pinned": false,
        "origin": "registry"
      },
      {
        "name": "rubocop",
        "version": null,
        "scopes": [
          "development"
        ],
        "source": "Gemfile",
        "pinned": false,
        "origin": "registry"
      }
    ],
    "transitive": []
  },
  "lockfiles": [
    "Gemfile.lock"
  ],
  "unparsed": []
}

Collected for ​

GuardrailCategoryInputs
ruby/lockfile-committedrubyprojectDir
ruby/no-git-dependenciesrubyprojectDir
ruby/single-gem-sourcerubyprojectDir
ruby/version-declaredrubyprojectDir

All collectors

Buildnote Limited
Registered in England and Wales, Reg: 16140412