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 key | ruby |
| Version | v1 |
| Script | ruby.py |
| Timeout | 30 seconds |
Inputs
| Input | Description | Default | Environment |
|---|---|---|---|
projectDir | Directory holding the project, relative to the directory the CLI runs in. | . | GUARDRAIL_INPUT_PROJECTDIR |
A guardrail that declares an input of the same name passes it through, so the guardrail's configuration in buildnote.json is what decides these values.
A guardrail asks for these facts by name, and reads them back the same way:
{
"collect": ["ruby"]
}ruby = guardrail.facts("ruby")Facts
The document ruby collects. A path carrying [] is an entry of the list before it, and one carrying [path] is a key of the object before it.
| Fact | Meaning |
|---|---|
directory | The projectDir input, as the guardrail configured it. |
exists | Whether that directory is there. Nothing else is collected when it is not. |
manifest | Path of the primary manifest, relative to projectDir: the Gemfile when there is one, otherwise the gemspec. |
sources | Every manifest that was read, in the order they were read. |
scanned | Every 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 a guardrail reading dependencies from one of these is reading a floor rather than the whole list. |
declared | The Ruby version the project asks for, or null when it asks for none. .ruby-version wins over the ruby directive, because it is what a developer's shell and the runner's version manager both read, and the ruby directive wins over a gemspec's required_ruby_version, which is the floor the gem supports rather than the version this checkout runs. |
declared.version | Constraint exactly as written, such as 3.3.1 or >= 3.2. |
declared.source | File declaring it, relative to projectDir. |
declared.pinned | Whether the constraint names one exact version rather than a range. A ~>, a >= or a * is not pinned. |
bundler | Version of Bundler Gemfile.lock records as the one that wrote it, or null when no lock file is committed. |
sources_declared | Every gem source the Gemfile names, such as https://rubygems.org. A second source is what a dependency confusion attack needs. |
projects | The one gem the checkout declares, or the project directory itself when it declares none. Bundler has no workspaces. |
projects[].path | Directory of the gem, relative to projectDir, always .. |
projects[].manifest | Its gemspec, relative to projectDir, or the Gemfile when the checkout carries no gemspec. |
projects[].name | Name the gemspec declares, or null when there is none to read it from. |
dependencies | What the manifests declare, split by whether the project asks for it itself. |
dependencies.direct | Every gem call the reader saw, in the order they appear. |
dependencies.direct[].name | Gem name, such as rails. |
dependencies.direct[].version | Requirement exactly as written, such as ~> 7.1, or null when the call names none and any version will do. |
dependencies.direct[].scopes | The 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[].source | Manifest declaring it, relative to projectDir. |
dependencies.direct[].pinned | Whether the requirement names one exact version. A ~>, a >=, a git: or a path: option is not pinned. |
dependencies.direct[].origin | Where it comes from: registry, git when the call carries a git: or github: option, or path when it carries a path: option. |
dependencies.transitive | Always empty. What Gemfile.lock resolves is not read: lockfiles says whether one is committed. |
lockfiles | Gemfile.lock when it is committed, relative to projectDir. An application without one resolves differently on every deploy. |
unparsed | Every manifest the reader could not open, so a guardrail can tell a project that declares nothing from one nobody could read. |
unparsed[].path | Path of that manifest, relative to projectDir. |
unparsed[].reason | What stopped the reader. |
A collector that cannot finish prints what it had along with an incomplete key saying why, so the facts below the point it stopped are absent. A check that cares reads incomplete before the rest.
Example facts
The example facts collected by ruby collector:
{
"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
| Guardrail | Category | Inputs |
|---|---|---|
ruby/lockfile-committed | ruby | projectDir |
ruby/no-git-dependencies | ruby | projectDir |
ruby/single-gem-source | ruby | projectDir |
ruby/version-declared | ruby | projectDir |