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
2 changes: 1 addition & 1 deletion .flox/env.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "catalog-check",
"name": "declaration-check",
"version": 1,
"env_id": "47ab46d7-8fac-4adf-8615-34b14a0cc0c6"
}
4 changes: 2 additions & 2 deletions .github/actions/check/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
# A composite action rather than a reusable workflow for the checks themselves:
# GitHub fetches an action's source while resolving `uses:`, so a caller needs
# nothing checked out but its own repository.
name: catalog-check
name: declaration-check
description: Run one of the checks every flox repository must pass.

inputs:
Expand All @@ -23,7 +23,7 @@ inputs:
path:
description: Declaration to read, relative to the repository root.
required: false
default: catalog-info.yaml
default: flox-info.yaml
python-version:
required: false
default: '3.12'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
# would reach private repositories only, and 83 of the live ones are public.
#
# Nothing here reads a credential, and nothing here needs to. See the action.
name: catalog-check
name: declaration-check

on:
# Kept callable directly so a repository can adopt the check ahead of the
Expand All @@ -19,7 +19,7 @@ on:
inputs:
path:
type: string
default: catalog-info.yaml
default: flox-info.yaml
pull_request:
push:
branches: [main]
Expand All @@ -33,7 +33,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
- uses: flox/catalog-check/.github/actions/check@2f04631c4c19ac18270d6589c8b6bf8ddbbfb504 # keep in step with main
- uses: flox/declaration-check/.github/actions/check@2f04631c4c19ac18270d6589c8b6bf8ddbbfb504 # keep in step with main
with:
check: declaration
path: ${{ inputs.path || 'catalog-info.yaml' }}
path: ${{ inputs.path || 'flox-info.yaml' }}
2 changes: 1 addition & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# This repository's own unit tests. Nothing else runs them, and no other
# repository cares about them — the check other repositories must pass is in
# catalog-check.yml.
# declaration-check.yml.
name: tests

# No paths filter, deliberately. A required check that is skipped never reports,
Expand Down
25 changes: 16 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,25 @@
# catalog-check
# declaration-check

Checks that a flox repository declares what it is and who owns it.

Every repository carries a `catalog-info.yaml` — a [Backstage entity
descriptor](https://backstage.io/docs/features/software-catalog/descriptor-format)
— saying how exposed it is, how much human review a dependency bump needs, its
maturity, and which area of responsibility owns it. This repository holds the
check that the declaration is present and complete, and nothing else.
Every repository carries a `flox-info.yaml` saying how exposed it is, how much
human review a dependency bump needs, its maturity, and which area of
responsibility owns it. This repository holds the check that the declaration is
present and complete, and nothing else.

The contents are a [Backstage entity
descriptor](https://backstage.io/docs/features/software-catalog/descriptor-format),
unchanged — the schema is theirs and worth keeping, so a Backstage instance could
read these as-is. Only the filename is ours. Backstage's own convention is
`catalog-info.yaml`, and "catalog" at flox means the Flox Catalog, which several
floxhub components serve; a file in every repository borrowing that word would
cost more in confusion than the default filename saves in configuration.

```yaml
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
name: catalog-check
name: declaration-check
labels:
flox.dev/classification: internal-tooling # customer-facing | distribution | internal-tooling | non-shipping | non-production
flox.dev/max-tier: manual # auto | assisted | manual
Expand All @@ -31,11 +38,11 @@ else notices.

## Running it

An organisation ruleset names `.github/workflows/catalog-check.yml` as a required
An organisation ruleset names `.github/workflows/declaration-check.yml` as a required
workflow, so repositories do not call it themselves. To run it against a checkout:

```
python3 declaration.py --path catalog-info.yaml
python3 declaration.py --path flox-info.yaml
```

It exits 0 when the declaration is complete, 1 with the fields that are missing or
Expand Down
8 changes: 5 additions & 3 deletions declaration.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
place that knows the format: everything downstream takes the dict this returns, so
changing the encoding later means changing this file and nothing else.

Today the encoding is a Backstage entity descriptor at `catalog-info.yaml`. That
Today the encoding is a Backstage entity descriptor at `flox-info.yaml` — their
schema, our filename, because `catalog-info.yaml` would put the Flox Catalog's
word in every repository. That
is an implementation detail, not a commitment — see ADR-001 and ADR-008's
withdrawal. Backstage's own schema validates the envelope; it does not validate
our facts, because `metadata.labels` is an open string map and will accept a
Expand Down Expand Up @@ -383,7 +385,7 @@ def is_expected_in_production(decl, today=None):
# explains why it is being asked.
# ---------------------------------------------------------------------------

DEFAULT_PATH = "catalog-info.yaml"
DEFAULT_PATH = "flox-info.yaml"

TEMPLATE = """apiVersion: backstage.io/v1alpha1
kind: Component
Expand Down Expand Up @@ -487,7 +489,7 @@ def _report_uncovered(args, gaps, declared):
"declared, because its %d other part%s %s."
% (declared, "" if declared == 1 else "s",
"is" if declared == 1 else "are"), file=out)
print("\nAdd a catalog-info.yaml in each, and list it in %s. Or, if one of the "
print("\nAdd a flox-info.yaml in each, and list it in %s. Or, if one of the "
"parts already declared should own it, move that declaration up to the "
"directory above both." % args.path, file=out)
_write_summary(args, "### Undeclared parts\n\n%s\n"
Expand Down
2 changes: 1 addition & 1 deletion catalog-info.yaml → flox-info.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
name: catalog-check
name: declaration-check
description: The check that every flox repository declares what it is and who owns it.
labels:
flox.dev/classification: internal-tooling
Expand Down
24 changes: 12 additions & 12 deletions test_declaration.py
Original file line number Diff line number Diff line change
Expand Up @@ -173,14 +173,14 @@ class TestMonorepoLocations(unittest.TestCase):
" name: example\n"
"spec:\n"
" targets:\n"
" - ./src/web-ui/catalog-info.yaml\n"
" - ./src/web-bff/catalog-info.yaml\n"
" - ./src/web-ui/flox-info.yaml\n"
" - ./src/web-bff/flox-info.yaml\n"
)

def test_targets_are_read_and_normalised(self):
self.assertEqual(
declaration.targets(self.LOCATION),
["src/web-ui/catalog-info.yaml", "src/web-bff/catalog-info.yaml"])
["src/web-ui/flox-info.yaml", "src/web-bff/flox-info.yaml"])

def test_a_component_is_not_a_location(self):
self.assertIsNone(declaration.targets(decl()))
Expand All @@ -200,7 +200,7 @@ def test_a_location_pointing_outside_the_repository_is_refused(self):
the property ADR-001 exists to get.
"""
external = (self.LOCATION.split("targets:")[0]
+ "targets:\n - https://example.com/catalog-info.yaml\n")
+ "targets:\n - https://example.com/flox-info.yaml\n")
with self.assertRaises(declaration.Invalid) as caught:
declaration.targets(external)
self.assertIn("not URLs", " ".join(caught.exception.problems))
Expand Down Expand Up @@ -376,20 +376,20 @@ def location(self, *targets):

def test_a_dotted_directory_keeps_its_dot(self):
self.assertEqual(
declaration.targets(self.location("./.github/catalog-info.yaml")),
[".github/catalog-info.yaml"])
declaration.targets(self.location("./.github/flox-info.yaml")),
[".github/flox-info.yaml"])

def test_a_dotted_directory_without_a_prefix_is_unchanged(self):
self.assertEqual(
declaration.targets(self.location(".github/catalog-info.yaml")),
[".github/catalog-info.yaml"])
declaration.targets(self.location(".github/flox-info.yaml")),
[".github/flox-info.yaml"])

def test_an_ordinary_relative_prefix_is_still_removed(self):
self.assertEqual(
declaration.targets(self.location("./src/web-ui/catalog-info.yaml")),
["src/web-ui/catalog-info.yaml"])
declaration.targets(self.location("./src/web-ui/flox-info.yaml")),
["src/web-ui/flox-info.yaml"])

def test_repeated_prefixes_are_removed_a_segment_at_a_time(self):
self.assertEqual(
declaration.targets(self.location("././.github/x/catalog-info.yaml")),
[".github/x/catalog-info.yaml"])
declaration.targets(self.location("././.github/x/flox-info.yaml")),
[".github/x/flox-info.yaml"])
Loading