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
12 changes: 11 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,15 @@ The format is (loosely) based on [Keep a Changelog](http://keepachangelog.com/)

## Unreleased

## [v1.11.0] - 2025-06-22

### Added

- Results summary for options that produce numerous results, ie. --collections, --item-collection, --recursive ([#138](https://github.com/stac-utils/stac-check/pull/138))
- Support for --verbose flag to show verbose results summary ([#138](https://github.com/stac-utils/stac-check/pull/138))
- Added `--output`/`-o` option to save validation results to a file ([#138](https://github.com/stac-utils/stac-check/pull/138))
- Tests for CLI options ([#138](https://github.com/stac-utils/stac-check/pull/138))

## [v1.10.1] - 2025-06-21

### Fixed
Expand Down Expand Up @@ -285,7 +294,8 @@ The format is (loosely) based on [Keep a Changelog](http://keepachangelog.com/)
- Validation from stac-validator 2.3.0
- Links and assets validation checks

[Unreleased]: https://github.com/stac-utils/stac-check/compare/v1.10.1...main
[Unreleased]: https://github.com/stac-utils/stac-check/compare/v1.11.0...main
[v1.11.0]: https://github.com/stac-utils/stac-check/compare/v1.10.1...v1.11.0
[v1.10.1]: https://github.com/stac-utils/stac-check/compare/v1.10.0...v1.10.1
[v1.10.0]: https://github.com/stac-utils/stac-check/compare/v1.9.1...v1.10.0
[v1.9.1]: https://github.com/stac-utils/stac-check/compare/v1.9.0...v1.9.1
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ Options:
multiple times.
--pydantic Use stac-pydantic for enhanced validation with Pydantic models.
--verbose Show verbose error messages.
-o, --output FILE Save output to the specified file.
--item-collection Validate item collection response. Can be combined with
--pages. Defaults to one page.
--collections Validate collections endpoint response. Can be combined with
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

from setuptools import find_packages, setup

__version__ = "1.10.1"
__version__ = "1.11.0"

with open("README.md", "r") as fh:
long_description = fh.read()
Expand Down
4 changes: 3 additions & 1 deletion stac_check/api_lint.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,13 @@ def __init__(
object_list_key: str,
pages: Optional[int] = 1,
headers: Optional[Dict] = None,
verbose: bool = False,
):
self.source = source
self.object_list_key = object_list_key
self.pages = pages if pages is not None else 1
self.headers = headers or {}
self.verbose = verbose
self.version = None
self.validator_version = self._get_validator_version()

Expand Down Expand Up @@ -148,7 +150,7 @@ def lint_all(self) -> List[Dict]:
results_by_url = {}
for obj, obj_url in self.iterate_objects():
try:
linter = Linter(obj)
linter = Linter(obj, verbose=self.verbose)
msg = dict(linter.message)
msg["path"] = obj_url
msg["best_practices"] = linter.best_practices_msg
Expand Down
149 changes: 106 additions & 43 deletions stac_check/cli.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import importlib.metadata
import sys
from typing import Optional

import click

Expand All @@ -12,6 +13,7 @@
recursive_message,
)
from stac_check.lint import Linter
from stac_check.utilities import handle_output


@click.option(
Expand Down Expand Up @@ -48,6 +50,12 @@
@click.option(
"-l", "--links", is_flag=True, help="Validate links for format and response."
)
@click.option(
"--output",
"-o",
type=click.Path(dir_okay=False, writable=True),
help="Save output to the specified file. Only works with --collections, --item-collection, or --recursive.",
)
@click.option(
"--no-assets-urls",
is_flag=True,
Expand All @@ -74,19 +82,44 @@
@click.argument("file")
@click.version_option(version=importlib.metadata.distribution("stac-check").version)
def main(
file,
collections,
item_collection,
pages,
recursive,
max_depth,
assets,
links,
no_assets_urls,
header,
pydantic,
verbose,
):
file: str,
collections: bool,
item_collection: bool,
pages: Optional[int],
recursive: bool,
max_depth: Optional[int],
assets: bool,
links: bool,
no_assets_urls: bool,
header: tuple[tuple[str, str], ...],
pydantic: bool,
verbose: bool,
output: Optional[str],
) -> None:
"""Main entry point for the stac-check CLI.

Args:
file: The STAC file or URL to validate
collections: Validate a collections endpoint
item_collection: Validate an item collection
pages: Number of pages to validate (for API endpoints)
recursive: Recursively validate linked STAC objects
max_depth: Maximum depth for recursive validation
assets: Validate assets
links: Validate links
no_assets_urls: Disable URL validation for assets
header: Additional HTTP headers
pydantic: Use stac-pydantic for validation
verbose: Show verbose output
output: Save output to file (only with --collections, --item-collection, or --recursive)
"""
# Check if output is used without --collections, --item-collection, or --recursive
if output and not any([collections, item_collection, recursive]):
click.echo(
"Error: --output can only be used with --collections, --item-collection, or --recursive",
err=True,
)
sys.exit(1)
# Check if pydantic validation is requested but not installed
if pydantic:
try:
Expand All @@ -99,8 +132,52 @@ def main(
)
pydantic = False

if not collections and not item_collection:
# Create a standard Linter for single file or recursive validation
if collections or item_collection:
# Handle API-based validation (collections or item collections)
api_linter = ApiLinter(
source=file,
object_list_key="collections" if collections else "features",
pages=pages if pages else 1,
headers=dict(header),
verbose=verbose,
)
results = api_linter.lint_all()

# Create a dummy Linter instance for display purposes
display_linter = Linter(
file,
assets=assets,
links=links,
headers=dict(header),
pydantic=pydantic,
verbose=verbose,
)

# Show intro message in the terminal
intro_message(display_linter)

# Define output generation function (without intro message since we already showed it)
def generate_output():
if collections:
collections_message(
api_linter,
results=results,
cli_message_func=cli_message,
verbose=verbose,
)
elif item_collection:
item_collection_message(
api_linter,
results=results,
cli_message_func=cli_message,
verbose=verbose,
)

# Handle output (without duplicating the intro message)
handle_output(output, generate_output)
sys.exit(0 if all(msg.get("valid_stac") is True for msg in results) else 1)
else:
# Handle file-based validation (single file or recursive)
linter = Linter(
file,
assets=assets,
Expand All @@ -112,34 +189,20 @@ def main(
pydantic=pydantic,
verbose=verbose,
)
intro_message(linter)
# If recursive validation is enabled, use recursive_message
if recursive:
# Pass the cli_message function to avoid circular imports
recursive_message(linter, cli_message_func=cli_message)
else:
# Otherwise, just display the standard CLI message
cli_message(linter)

sys.exit(0 if linter.valid_stac else 1)
else:
if item_collection:
object_list_key = "features"
elif collections:
object_list_key = "collections"
intro_message(linter)

linter = ApiLinter(
source=file,
object_list_key=object_list_key,
pages=pages,
headers=dict(header),
)
results = linter.lint_all()
# Show intro message in the terminal
intro_message(linter)
if collections:
collections_message(linter, results=results, cli_message_func=cli_message)
elif item_collection:
item_collection_message(
linter, results=results, cli_message_func=cli_message
)
sys.exit(0 if all(msg.get("valid_stac") is True for msg in results) else 1)

# Define output generation function (without intro message since we already showed it)
def generate_output():
if recursive:
recursive_message(linter, cli_message_func=cli_message, verbose=verbose)
else:
cli_message(linter)

# Handle output (without duplicating the intro message)
handle_output(output if recursive else None, generate_output)

sys.exit(0 if linter.valid_stac else 1)
Loading