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
77 changes: 3 additions & 74 deletions src/cfengine_cli/commands.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,14 @@
import sys
import os
import re
import json
import yaml
from cfengine_cli.profile import profile_cfengine, generate_callstack
from cfengine_cli.dev import dispatch_dev_subcommand
from cfengine_cli.lint import lint_args, PolicySyntaxError
from cfengine_cli.lint import lint_args
from cfengine_cli.shell import user_command
from cfengine_cli.paths import bin
from cfengine_cli.version import cfengine_cli_version_string
from cfengine_cli.format import (
format_policy_file,
format_json_file,
format_policy_fin_fout,
)
from cfengine_cli.format import format_paths
from cfengine_cli.utils import UserError
from cfengine_cli.up import validate_config
from cfbs.commands import build_command
Expand Down Expand Up @@ -51,74 +46,8 @@ def deploy() -> int:
return r


def _format_filename(filename: str, line_length: int, check: bool) -> int:
"""Format a single file.

Raises PolicySyntaxError for .cf files with syntax errors."""
if filename.endswith(".json"):
return format_json_file(filename, check)
if filename.endswith(".cf"):
return format_policy_file(filename, line_length, check)
raise UserError(f"Unrecognized file format: {filename}")


def _format_dirname(directory: str, line_length: int, check: bool) -> int:
ret = 0
for root, dirs, files in os.walk(directory):
# Don't recurse into hidden folders
dirs[:] = [d for d in dirs if not d.startswith(".")]
for name in sorted(files):
if name.startswith("."):
continue # Hidden files are ignored by default
if (
name.endswith(".x.cf")
or name.endswith(".input.cf")
or name.endswith(".output.cf")
or name.endswith(".expected.cf")
):
continue # Test files skipped during directory traversal
if name.endswith(
(".input.json", ".jqinput.json", ".x.json", ".expected.json")
):
continue # Test files skipped during directory traversal
filepath = os.path.join(root, name)
if name.endswith(".json") or name.endswith(".cf"):
ret |= _format_filename(filepath, line_length, check)
return ret


def format(names, line_length, check) -> int:
try:
return _format_inner(names, line_length, check)
except PolicySyntaxError as e:
print(f"Error: {e}")
return 1


def _format_inner(names, line_length, check) -> int:
if not names:
return _format_dirname(".", line_length, check)
if len(names) == 1 and names[0] == "-":
# Special case, format policy file from stdin to stdout
return format_policy_fin_fout(sys.stdin, sys.stdout, line_length, check)

ret = 0
for name in names:
if name == "-":
raise UserError(
"The - argument has a special meaning and cannot be combined with other paths"
)
if not os.path.exists(name):
raise UserError(f"{name} does not exist")
if os.path.isfile(name):
ret |= _format_filename(name, line_length, check)
continue
if os.path.isdir(name):
ret |= _format_dirname(name, line_length, check)
continue
if check:
return ret
return 0
return format_paths(names, line_length, check)


def _lint(files, strict) -> int:
Expand Down
17 changes: 13 additions & 4 deletions src/cfengine_cli/dev.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
print_release_dependency_tables,
)
from cfengine_cli.docs import update_docs, check_docs
from cfengine_cli.format import format_paths
from cfengine_cli.syntax_tree import syntax_tree


Expand Down Expand Up @@ -49,12 +50,20 @@ def print_dependency_tables(args) -> int:

def format_docs(files) -> int:
_expect_repo("documentation")
return update_docs(files)
ret = update_docs(files)
# Also run the same logic as `cfengine format` so .cf / .json files
# are formatted without having to run that command manually.
ret |= format_paths(files, line_length=80, check=False)
return ret


def lint_docs() -> int:
def lint_docs(files) -> int:
_expect_repo("documentation")
return check_docs()
ret = check_docs()
# Also run the same logic as `cfengine format --check` so .cf / .json
# files are checked without having to run that command manually.
ret |= format_paths(files, line_length=80, check=True)
return ret


def generate_release_information(
Expand All @@ -73,7 +82,7 @@ def dispatch_dev_subcommand(subcommand, args) -> int:
if subcommand == "format-docs":
return format_docs(args.files)
if subcommand == "lint-docs":
return lint_docs()
return lint_docs(args.files)
if subcommand == "syntax-tree":
return syntax_tree(args.file)
if subcommand == "generate-release-information":
Expand Down
81 changes: 80 additions & 1 deletion src/cfengine_cli/format.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,14 @@
from typing import IO

import json
import os
import sys

import tree_sitter_cfengine as tscfengine
from tree_sitter import Language, Parser, Node
from cfbs.pretty import pretty_file, pretty_check_file
from cfengine_cli.lint import check_policy_syntax
from cfengine_cli.lint import check_policy_syntax, PolicySyntaxError
from cfengine_cli.utils import UserError

# Node types that increase indentation by 2 when entered
INDENTED_TYPES = {
Expand Down Expand Up @@ -985,3 +988,79 @@ def format_policy_fin_fout(
new_data = fmt.buffer + "\n"
fout.write(new_data)
return 0


def _format_filename(filename: str, line_length: int, check: bool) -> int:
"""Format a single file.

Raises PolicySyntaxError for .cf files with syntax errors."""
if filename.endswith(".json"):
return format_json_file(filename, check)
if filename.endswith(".cf"):
return format_policy_file(filename, line_length, check)
raise UserError(f"Unrecognized file format: {filename}")


def _format_dirname(directory: str, line_length: int, check: bool) -> int:
ret = 0
for root, dirs, files in os.walk(directory):
# Don't recurse into hidden folders
dirs[:] = [d for d in dirs if not d.startswith(".")]
for name in sorted(files):
if name.startswith("."):
continue # Hidden files are ignored by default
if (
name.endswith(".x.cf")
or name.endswith(".input.cf")
or name.endswith(".output.cf")
or name.endswith(".expected.cf")
):
continue # Test files skipped during directory traversal
if name.endswith(
(".input.json", ".jqinput.json", ".x.json", ".expected.json")
):
continue # Test files skipped during directory traversal
filepath = os.path.join(root, name)
if name.endswith(".json") or name.endswith(".cf"):
ret |= _format_filename(filepath, line_length, check)
return ret


def _format_paths_inner(names, line_length, check) -> int:
if not names:
return _format_dirname(".", line_length, check)
if len(names) == 1 and names[0] == "-":
# Special case, format policy file from stdin to stdout
return format_policy_fin_fout(sys.stdin, sys.stdout, line_length, check)

ret = 0
for name in names:
if name == "-":
raise UserError(
"The - argument has a special meaning and cannot be combined with other paths"
)
if not os.path.exists(name):
raise UserError(f"{name} does not exist")
if os.path.isfile(name):
ret |= _format_filename(name, line_length, check)
continue
if os.path.isdir(name):
ret |= _format_dirname(name, line_length, check)
continue
if check:
return ret
return 0


def format_paths(names, line_length, check) -> int:
"""Format the given paths (files and/or directories), or stdin.

With no names, formats the current directory. A single "-" formats
policy from stdin to stdout. Returns 0 on success (or no reformat
needed), 1 when reformatting is needed in check mode or a policy file
has syntax errors."""
try:
return _format_paths_inner(names, line_length, check)
except PolicySyntaxError as e:
print(f"Error: {e}")
return 1
18 changes: 9 additions & 9 deletions uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading