Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,17 @@

### Added

- New `packages` fact: the installed packages on the host, namespaced by the
package database they come from — `packages.dpkg`, `packages.rpm`,
`packages.pacman`, `packages.apk`, `packages.snap`, `packages.flatpak`,
`packages.nix` (Linux); `packages.pkg` (FreeBSD/DragonFly),
`packages.openbsd_pkg`, `packages.pkgsrc`, `packages.ips`; `packages.receipts`,
`packages.apps`, `packages.homebrew` (macOS); and `packages.registry`,
`packages.appx` (Windows). Each source is an array of `{name, version, ...}`
records with per-source identity fields (architecture, product_code, bundle_id,
and so on); sources are never merged, and a source is omitted when its database
is absent. Scope is system package databases (language/runtime managers are out
of scope). The whole probe is skippable with `--disable packages`.
- Fact disabling is now a first-class, facts-native control. Disable any fact
or fact group with `--disable a,b`, the `FACTS_DISABLE=a,b` environment
variable, or the `facts.conf` `disable` key; the Facter `blocklist` key keeps
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

<br>

Facts is a Go port of [Puppet Facter](https://github.com/puppetlabs/facter). It discovers facts about the system it runs on — hardware, networking, OS, cloud metadata — and serves them two ways: as an embeddable Go library, and as the `facts` CLI. It reads your existing facter configuration, fact directories, and `FACTER_*` environment facts as the compatibility tier of its input surface, so facter-configured hosts keep working unchanged.
Facts is a Go port of [Puppet Facter](https://github.com/puppetlabs/facter). It discovers facts about the system it runs on — hardware, networking, OS, installed packages, cloud metadata — and serves them two ways: as an embeddable Go library, and as the `facts` CLI. It reads your existing facter configuration, fact directories, and `FACTER_*` environment facts as the compatibility tier of its input surface, so facter-configured hosts keep working unchanged.

Ruby Facter compatibility is promised exactly where it matters and nowhere else: at the CLI process boundary (the output contract) and for operator-supplied fact sources — external facts and `facter.conf` (the input contract, now read under facts-native names first; see [the input-compatibility reference](docs/FACTER_CONF_COMPATIBILITY.md)). Ruby DSL fact files are not read; rewrite them as external facts ([migration guide](docs/CUSTOM_FACT_MIGRATION.md)). The Go API itself is just Go.

Expand Down
81 changes: 81 additions & 0 deletions docs/schema/facts.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -795,6 +795,87 @@ os.windows.system32:
description: The native system32 directory, sysnative-aware for 32-bit processes.
platforms: [windows]

packages.apk:
type: array
description: Installed Alpine apk packages as {name, version, architecture} records, from /lib/apk/db/installed.
platforms: [linux]
conditional: true
packages.appx:
type: array
description: Windows AppX/MSIX packages as {name, version, architecture} records — the system-provisioned set plus the collector context's packages, deduplicated.
platforms: [windows]
conditional: true
packages.apps:
type: array
description: macOS application bundles as {name, version, bundle_id, path} records; secondary to receipts and never merged with it.
platforms: [darwin]
conditional: true
packages.dpkg:
type: array
description: Installed dpkg packages as {name, version, architecture} records (installed-state entries including held and trigger states; multiarch siblings kept), from /var/lib/dpkg/status.
platforms: [linux]
conditional: true
packages.flatpak:
type: array
description: Flatpak applications and runtimes from the system and collector-user installations as {name, version, architecture, branch} records; branch distinguishes same-version siblings.
platforms: [linux]
conditional: true
packages.homebrew:
type: array
description: Homebrew formulae and casks as {name, version, type, prefix} records from every detected prefix (/opt/homebrew, /usr/local); prefix distinguishes dual-install duplicates.
platforms: [darwin]
conditional: true
packages.ips:
type: array
description: Installed illumos IPS packages as {name, version} records, from the local image (no network refresh).
platforms: [illumos]
conditional: true
packages.nix:
type: array
description: Installed Nix packages as {name, version} records — the NixOS system profile set, or the default profile (/nix/var/nix/profiles/default) on non-NixOS hosts; never the whole /nix/store.
platforms: [linux, darwin]
conditional: true
packages.openbsd_pkg:
type: array
description: Installed OpenBSD packages as {name, version, architecture} records, from /var/db/pkg (architecture from each package's +CONTENTS @arch; omitted for arch-independent packages).
platforms: [openbsd]
conditional: true
packages.pacman:
type: array
description: Installed pacman packages as {name, version, architecture} records, from /var/lib/pacman/local.
platforms: [linux]
conditional: true
packages.pkg:
type: array
description: Installed pkgng packages as {name, version, architecture} records (shared by FreeBSD and DragonFly).
platforms: [freebsd, dragonfly]
conditional: true
packages.pkgsrc:
type: array
description: Installed pkgsrc packages as {name, version} records, from the discovered PKG_DBDIR; NetBSD's primary source and an illumos/SmartOS and DragonFly secondary.
platforms: [netbsd, dragonfly, illumos]
conditional: true
packages.receipts:
type: array
description: macOS installer(8)/PackageKit .pkg receipts as {name, version} records, from /var/db/receipts; the primary macOS source.
platforms: [darwin]
conditional: true
packages.registry:
type: array
description: Windows uninstall entries from both HKLM hives as {name, version, product_code, architecture} records.
platforms: [windows]
conditional: true
packages.rpm:
type: array
description: Installed rpm packages as {name, version, architecture} records (epoch preserved, gpg-pubkey filtered).
platforms: [linux]
conditional: true
packages.snap:
type: array
description: Installed snap packages as {name, version} records.
platforms: [linux]
conditional: true

partitions.*:
type: map
description: A disk partition (or device-mapper/loop device), keyed by device path.
Expand Down
16 changes: 8 additions & 8 deletions docs/supported-facts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@ These pages are generated from [`docs/schema/facts.yaml`](../schema/facts.yaml).

| Platform | Supported facts |
| --- | ---: |
| [Linux](linux.md) | 175 |
| [macOS / Darwin](darwin.md) | 107 |
| [Windows](windows.md) | 101 |
| [FreeBSD](freebsd.md) | 131 |
| [OpenBSD](openbsd.md) | 113 |
| [NetBSD](netbsd.md) | 117 |
| [DragonFly BSD](dragonfly.md) | 115 |
| [illumos](illumos.md) | 114 |
| [Linux](linux.md) | 182 |
| [macOS / Darwin](darwin.md) | 111 |
| [Windows](windows.md) | 103 |
| [FreeBSD](freebsd.md) | 132 |
| [OpenBSD](openbsd.md) | 114 |
| [NetBSD](netbsd.md) | 118 |
| [DragonFly BSD](dragonfly.md) | 117 |
| [illumos](illumos.md) | 116 |
| [Plan 9](plan9.md) | 29 |
6 changes: 5 additions & 1 deletion docs/supported-facts/darwin.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ $ facts --json

## Fact Contract

107 schema entries include `darwin`.
111 schema entries include `darwin`.

| Fact | Type | Conditional | Description |
| --- | --- | --- | --- |
Expand Down Expand Up @@ -153,6 +153,10 @@ $ facts --json
| `os.release.full` | `string` | no | The full release number of the operating system. |
| `os.release.major` | `string` | no | The major release number of the operating system. |
| `os.release.minor` | `string` | yes | The minor release number of the operating system, when it has one. |
| `packages.apps` | `array` | yes | macOS application bundles as {name, version, bundle_id, path} records; secondary to receipts and never merged with it. |
| `packages.homebrew` | `array` | yes | Homebrew formulae and casks as {name, version, type, prefix} records from every detected prefix (/opt/homebrew, /usr/local); prefix distinguishes dual-install duplicates. |
| `packages.nix` | `array` | yes | Installed Nix packages as {name, version} records — the NixOS system profile set, or the default profile (/nix/var/nix/profiles/default) on non-NixOS hosts; never the whole /nix/store. |
| `packages.receipts` | `array` | yes | macOS installer(8)/PackageKit .pkg receipts as {name, version} records, from /var/db/receipts; the primary macOS source. |
| `path` | `array` | no | The PATH environment entries of the Facts process, in lookup order. |
| `processors.cores` | `integer` | no | The number of cores per processor socket. |
| `processors.count` | `integer` | yes | The number of logical processors. |
Expand Down
4 changes: 3 additions & 1 deletion docs/supported-facts/dragonfly.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ $ facts --json

## Fact Contract

115 schema entries include `dragonfly`.
117 schema entries include `dragonfly`.

| Fact | Type | Conditional | Description |
| --- | --- | --- | --- |
Expand Down Expand Up @@ -161,6 +161,8 @@ $ facts --json
| `os.release.full` | `string` | no | The full release number of the operating system. |
| `os.release.major` | `string` | no | The major release number of the operating system. |
| `os.release.minor` | `string` | yes | The minor release number of the operating system, when it has one. |
| `packages.pkg` | `array` | yes | Installed pkgng packages as {name, version, architecture} records (shared by FreeBSD and DragonFly). |
| `packages.pkgsrc` | `array` | yes | Installed pkgsrc packages as {name, version} records, from the discovered PKG_DBDIR; NetBSD's primary source and an illumos/SmartOS and DragonFly secondary. |
| `partitions.*` | `map` | yes | A disk partition (or device-mapper/loop device), keyed by device path. |
| `partitions.*.filesystem` | `string` | yes | The filesystem type of the partition. |
| `partitions.*.mount` | `string` | yes | The path the partition is mounted on. |
Expand Down
3 changes: 2 additions & 1 deletion docs/supported-facts/freebsd.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ $ facts --json

## Fact Contract

131 schema entries include `freebsd`.
132 schema entries include `freebsd`.

| Fact | Type | Conditional | Description |
| --- | --- | --- | --- |
Expand Down Expand Up @@ -166,6 +166,7 @@ $ facts --json
| `os.release.major` | `string` | no | The major release number of the operating system. |
| `os.release.minor` | `string` | yes | The minor release number of the operating system, when it has one. |
| `os.release.patchlevel` | `string` | yes | The FreeBSD patch level of the installed userland. |
| `packages.pkg` | `array` | yes | Installed pkgng packages as {name, version, architecture} records (shared by FreeBSD and DragonFly). |
| `partitions.*` | `map` | yes | A disk partition (or device-mapper/loop device), keyed by device path. |
| `partitions.*.filesystem` | `string` | yes | The filesystem type of the partition. |
| `partitions.*.mount` | `string` | yes | The path the partition is mounted on. |
Expand Down
4 changes: 3 additions & 1 deletion docs/supported-facts/illumos.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ $ facts --json

## Fact Contract

114 schema entries include `illumos`.
116 schema entries include `illumos`.

| Fact | Type | Conditional | Description |
| --- | --- | --- | --- |
Expand Down Expand Up @@ -178,6 +178,8 @@ $ facts --json
| `os.name` | `string` | no | The operating system name, such as Ubuntu, Darwin, windows, or Plan 9. |
| `os.release.full` | `string` | no | The full release number of the operating system. |
| `os.release.major` | `string` | no | The major release number of the operating system. |
| `packages.ips` | `array` | yes | Installed illumos IPS packages as {name, version} records, from the local image (no network refresh). |
| `packages.pkgsrc` | `array` | yes | Installed pkgsrc packages as {name, version} records, from the discovered PKG_DBDIR; NetBSD's primary source and an illumos/SmartOS and DragonFly secondary. |
| `partitions.*` | `map` | yes | A disk partition (or device-mapper/loop device), keyed by device path. |
| `partitions.*.filesystem` | `string` | yes | The filesystem type of the partition. |
| `partitions.*.size` | `string` | yes | The display size of the partition, such as 1.00 GiB. |
Expand Down
9 changes: 8 additions & 1 deletion docs/supported-facts/linux.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ $ facts --json

## Fact Contract

175 schema entries include `linux`.
182 schema entries include `linux`.

| Fact | Type | Conditional | Description |
| --- | --- | --- | --- |
Expand Down Expand Up @@ -217,6 +217,13 @@ $ facts --json
| `os.selinux.enabled` | `boolean` | no | Whether SELinux is enabled. |
| `os.selinux.enforced` | `boolean` | yes | Whether SELinux is enforcing, when SELinux is enabled. |
| `os.selinux.policy_version` | `string` | yes | The loaded SELinux policy version, when SELinux is enabled. |
| `packages.apk` | `array` | yes | Installed Alpine apk packages as {name, version, architecture} records, from /lib/apk/db/installed. |
| `packages.dpkg` | `array` | yes | Installed dpkg packages as {name, version, architecture} records (installed-state entries including held and trigger states; multiarch siblings kept), from /var/lib/dpkg/status. |
| `packages.flatpak` | `array` | yes | Flatpak applications and runtimes from the system and collector-user installations as {name, version, architecture, branch} records; branch distinguishes same-version siblings. |
| `packages.nix` | `array` | yes | Installed Nix packages as {name, version} records — the NixOS system profile set, or the default profile (/nix/var/nix/profiles/default) on non-NixOS hosts; never the whole /nix/store. |
| `packages.pacman` | `array` | yes | Installed pacman packages as {name, version, architecture} records, from /var/lib/pacman/local. |
| `packages.rpm` | `array` | yes | Installed rpm packages as {name, version, architecture} records (epoch preserved, gpg-pubkey filtered). |
| `packages.snap` | `array` | yes | Installed snap packages as {name, version} records. |
| `partitions.*` | `map` | yes | A disk partition (or device-mapper/loop device), keyed by device path. |
| `partitions.*.backing_file` | `string` | yes | The file backing the loop device. |
| `partitions.*.filesystem` | `string` | yes | The filesystem type of the partition. |
Expand Down
3 changes: 2 additions & 1 deletion docs/supported-facts/netbsd.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ $ facts --json

## Fact Contract

117 schema entries include `netbsd`.
118 schema entries include `netbsd`.

| Fact | Type | Conditional | Description |
| --- | --- | --- | --- |
Expand Down Expand Up @@ -175,6 +175,7 @@ $ facts --json
| `os.release.full` | `string` | no | The full release number of the operating system. |
| `os.release.major` | `string` | no | The major release number of the operating system. |
| `os.release.minor` | `string` | yes | The minor release number of the operating system, when it has one. |
| `packages.pkgsrc` | `array` | yes | Installed pkgsrc packages as {name, version} records, from the discovered PKG_DBDIR; NetBSD's primary source and an illumos/SmartOS and DragonFly secondary. |
| `partitions.*` | `map` | yes | A disk partition (or device-mapper/loop device), keyed by device path. |
| `partitions.*.filesystem` | `string` | yes | The filesystem type of the partition. |
| `partitions.*.mount` | `string` | yes | The path the partition is mounted on. |
Expand Down
3 changes: 2 additions & 1 deletion docs/supported-facts/openbsd.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ $ facts --json

## Fact Contract

113 schema entries include `openbsd`.
114 schema entries include `openbsd`.

| Fact | Type | Conditional | Description |
| --- | --- | --- | --- |
Expand Down Expand Up @@ -156,6 +156,7 @@ $ facts --json
| `os.release.full` | `string` | no | The full release number of the operating system. |
| `os.release.major` | `string` | no | The major release number of the operating system. |
| `os.release.minor` | `string` | yes | The minor release number of the operating system, when it has one. |
| `packages.openbsd_pkg` | `array` | yes | Installed OpenBSD packages as {name, version, architecture} records, from /var/db/pkg (architecture from each package's +CONTENTS @arch; omitted for arch-independent packages). |
| `partitions.*` | `map` | yes | A disk partition (or device-mapper/loop device), keyed by device path. |
| `partitions.*.filesystem` | `string` | yes | The filesystem type of the partition. |
| `partitions.*.mount` | `string` | yes | The path the partition is mounted on. |
Expand Down
4 changes: 3 additions & 1 deletion docs/supported-facts/windows.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ $ facts --json

## Fact Contract

101 schema entries include `windows`.
103 schema entries include `windows`.

| Fact | Type | Conditional | Description |
| --- | --- | --- | --- |
Expand Down Expand Up @@ -134,6 +134,8 @@ $ facts --json
| `os.windows.product_name` | `string` | no | The Windows product name, such as Windows Server 2022 Datacenter. |
| `os.windows.release_id` | `string` | no | The Windows release identifier (the display version when available). |
| `os.windows.system32` | `string` | no | The native system32 directory, sysnative-aware for 32-bit processes. |
| `packages.appx` | `array` | yes | Windows AppX/MSIX packages as {name, version, architecture} records — the system-provisioned set plus the collector context's packages, deduplicated. |
| `packages.registry` | `array` | yes | Windows uninstall entries from both HKLM hives as {name, version, product_code, architecture} records. |
| `path` | `array` | no | The PATH environment entries of the Facts process, in lookup order. |
| `processors.cores` | `integer` | no | The number of cores per processor socket. |
| `processors.count` | `integer` | yes | The number of logical processors. |
Expand Down
4 changes: 4 additions & 0 deletions internal/engine/builtin_groups_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ func TestBuiltinFactGroups_keepStructuredRootsDropLegacyFlatNames(t *testing.T)
"operating system": "os",
"processor": "processors",
"path": "path",
// Facts-native group (ADR-0014): a deliberate parity divergence from
// Ruby Facter's group list, so `--list-block-groups` names packages as
// one disable unit.
"packages": "packages",
}
for name, root := range wantRoot {
facts, ok := groups[name]
Expand Down
1 change: 1 addition & 0 deletions internal/engine/core.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ func buildCoreFacts(s *Session, disabled map[string]bool) []ResolvedFact {
gate("timezone", timezoneCoreFacts)
gate("augeas", augeasCoreFacts)
gate("xen", xenCoreFacts)
gate("packages", packagesCoreFacts)
facts = append(facts, currentLinuxHypervisorFacts(s)...)
facts = append(facts, currentWindowsHypervisorFacts(s)...)
facts = append(facts, azureFacts(s.Context(), newAzureClient(azureMetadataBaseURL, nil), virtualization)...)
Expand Down
10 changes: 8 additions & 2 deletions internal/engine/core_gating_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ func hasRoot(facts []ResolvedFact, root string) bool {
// top-level fact name that gates it (ADR-0015).
var gatedSingleOutputCategories = []string{
"networking", "processors", "memory", "ssh",
"timezone", "fips_enabled", "augeas", "xen",
"timezone", "fips_enabled", "augeas", "xen", "packages",
}

func TestBuildCoreFacts_resolutionGatesSingleOutputCategories(t *testing.T) {
Expand All @@ -41,7 +41,13 @@ func TestBuildCoreFacts_resolutionGatesSingleOutputCategories(t *testing.T) {
// assert gating for the ones that do produce output by default.
continue
}
gated := buildCoreFacts(NewSession(), map[string]bool{fact: true})
// Each gated build also disables packages: its probe is the most
// expensive (plutil over every .app; PowerShell spawns on Windows
// runners), and gates are independent, so disabling it alongside does
// not affect the fact under test — it just keeps ~9 full package
// probes out of every CI run. The packages iteration itself still
// asserts its own gating.
gated := buildCoreFacts(NewSession(), map[string]bool{fact: true, "packages": true})
if hasRoot(gated, fact) {
t.Fatalf("buildCoreFacts(disabled=%q) still emitted %q root; resolver was not gated", fact, fact)
}
Expand Down
Loading
Loading