Convert shell scripts to Python with Joomla/Dolibarr platform support #32
31
.github/workflows/ci.yml
vendored
@@ -36,31 +36,36 @@ jobs:
|
||||
run: |
|
||||
git config --global core.autocrlf false
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.x'
|
||||
|
||||
- name: Verify script executability
|
||||
run: |
|
||||
chmod +x scripts/**/*.sh || true
|
||||
chmod +x scripts/**/*.py || true
|
||||
|
||||
- name: Required validations
|
||||
run: |
|
||||
set -e
|
||||
|
||||
scripts/validate/manifest.sh
|
||||
scripts/validate/xml_wellformed.sh
|
||||
scripts/validate/workflows.sh
|
||||
python3 scripts/validate/manifest.py
|
||||
python3 scripts/validate/xml_wellformed.py
|
||||
python3 scripts/validate/workflows.py
|
||||
|
||||
- name: Optional validations
|
||||
run: |
|
||||
set +e
|
||||
|
||||
scripts/validate/changelog.sh
|
||||
scripts/validate/language_structure.sh
|
||||
scripts/validate/license_headers.sh
|
||||
scripts/validate/no_secrets.sh
|
||||
scripts/validate/paths.sh
|
||||
scripts/validate/php_syntax.sh
|
||||
scripts/validate/tabs.sh
|
||||
scripts/validate/version_alignment.sh
|
||||
scripts/validate/version_hierarchy.sh
|
||||
python3 scripts/validate/changelog.py || echo "changelog validation not yet converted"
|
||||
python3 scripts/validate/language_structure.py || echo "language_structure validation not yet converted"
|
||||
python3 scripts/validate/license_headers.py || echo "license_headers validation not yet converted"
|
||||
python3 scripts/validate/no_secrets.py
|
||||
python3 scripts/validate/paths.py
|
||||
python3 scripts/validate/php_syntax.py
|
||||
python3 scripts/validate/tabs.py
|
||||
python3 scripts/validate/version_alignment.py || echo "version_alignment validation not yet converted"
|
||||
python3 scripts/validate/version_hierarchy.py || echo "version_hierarchy validation not yet converted"
|
||||
|
||||
- name: CI summary
|
||||
if: always()
|
||||
|
||||
17
.github/workflows/deploy_staging.yml
vendored
@@ -53,26 +53,31 @@ jobs:
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.x'
|
||||
|
||||
- name: Run pre-deployment validations
|
||||
run: |
|
||||
chmod +x scripts/validate/*.sh
|
||||
chmod +x scripts/validate/*.py
|
||||
|
||||
# Required validations
|
||||
scripts/validate/manifest.sh
|
||||
scripts/validate/xml_wellformed.sh
|
||||
scripts/validate/php_syntax.sh
|
||||
python3 scripts/validate/manifest.py
|
||||
python3 scripts/validate/xml_wellformed.py
|
||||
python3 scripts/validate/php_syntax.py
|
||||
|
||||
- name: Build deployment package
|
||||
id: build
|
||||
run: |
|
||||
chmod +x scripts/release/package_extension.sh
|
||||
chmod +x scripts/release/package_extension.py
|
||||
|
||||
VERSION="${{ inputs.version }}"
|
||||
if [ -z "${VERSION}" ]; then
|
||||
VERSION=$(grep -oP '<version>\K[^<]+' src/templates/templateDetails.xml | head -n 1)
|
||||
fi
|
||||
|
||||
scripts/release/package_extension.sh dist "${VERSION}"
|
||||
python3 scripts/release/package_extension.py dist "${VERSION}"
|
||||
|
||||
ZIP_FILE=$(ls dist/*.zip | head -n 1)
|
||||
echo "package=${ZIP_FILE}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
47
.github/workflows/release_pipeline.yml
vendored
@@ -103,21 +103,20 @@ jobs:
|
||||
# Check if REF_NAME is main or matches version pattern
|
||||
if [ "${REF_NAME}" = "main" ]; then
|
||||
# Infer version from manifest when on main branch
|
||||
SCRIPT_LIB_DIR="${GITHUB_WORKSPACE}/scripts/lib"
|
||||
if [ ! -f "${SCRIPT_LIB_DIR}/joomla_manifest.sh" ]; then
|
||||
echo "ERROR: Cannot find joomla_manifest.sh library" >> "${GITHUB_STEP_SUMMARY}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Source the library functions
|
||||
. "${SCRIPT_LIB_DIR}/joomla_manifest.sh"
|
||||
|
||||
# Find and extract version from manifest
|
||||
MANIFEST="$(find_manifest "${GITHUB_WORKSPACE}/src")"
|
||||
VERSION="$(get_manifest_version "${MANIFEST}")"
|
||||
# Use Python library for cross-platform compatibility
|
||||
VERSION=$(python3 -c "
|
||||
import sys
|
||||
sys.path.insert(0, '${GITHUB_WORKSPACE}/scripts/lib')
|
||||
import extension_utils
|
||||
ext_info = extension_utils.get_extension_info('${GITHUB_WORKSPACE}/src')
|
||||
if ext_info:
|
||||
print(ext_info.version)
|
||||
else:
|
||||
sys.exit(1)
|
||||
")
|
||||
|
||||
if [ -z "${VERSION}" ]; then
|
||||
echo "ERROR: Failed to extract version from manifest: ${MANIFEST}" >> "${GITHUB_STEP_SUMMARY}"
|
||||
echo "ERROR: Failed to extract version from manifest" >> "${GITHUB_STEP_SUMMARY}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
|
||||
@@ -552,19 +551,19 @@ jobs:
|
||||
set -euo pipefail
|
||||
|
||||
required_scripts=(
|
||||
"scripts/validate/manifest.sh"
|
||||
"scripts/validate/xml_wellformed.sh"
|
||||
"scripts/validate/manifest.py"
|
||||
"scripts/validate/xml_wellformed.py"
|
||||
)
|
||||
|
||||
optional_scripts=(
|
||||
"scripts/validate/changelog.sh"
|
||||
"scripts/validate/language_structure.sh"
|
||||
"scripts/validate/license_headers.sh"
|
||||
"scripts/validate/no_secrets.sh"
|
||||
"scripts/validate/paths.sh"
|
||||
"scripts/validate/php_syntax.sh"
|
||||
"scripts/validate/tabs.sh"
|
||||
"scripts/validate/version_alignment.sh"
|
||||
"scripts/validate/changelog.py"
|
||||
"scripts/validate/language_structure.py"
|
||||
"scripts/validate/license_headers.py"
|
||||
"scripts/validate/no_secrets.py"
|
||||
"scripts/validate/paths.py"
|
||||
"scripts/validate/php_syntax.py"
|
||||
"scripts/validate/tabs.py"
|
||||
"scripts/validate/version_alignment.py"
|
||||
)
|
||||
|
||||
missing=()
|
||||
@@ -596,7 +595,7 @@ jobs:
|
||||
for s in "${required_scripts[@]}" "${optional_scripts[@]}"; do
|
||||
if [ -f "${s}" ]; then
|
||||
chmod +x "${s}"
|
||||
"${s}" >> "${GITHUB_STEP_SUMMARY}"
|
||||
python3 "${s}" >> "${GITHUB_STEP_SUMMARY}"
|
||||
ran+=("${s}")
|
||||
else
|
||||
skipped+=("${s}")
|
||||
|
||||
23
Makefile
@@ -34,14 +34,14 @@ install:
|
||||
## validate: Run all validation scripts
|
||||
validate:
|
||||
@echo "Running validation scripts..."
|
||||
@./scripts/run/validate_all.sh
|
||||
@python3 ./scripts/run/validate_all.py
|
||||
|
||||
## validate-required: Run only required validation scripts
|
||||
validate-required:
|
||||
@echo "Running required validations..."
|
||||
@./scripts/validate/manifest.sh
|
||||
@./scripts/validate/xml_wellformed.sh
|
||||
@./scripts/validate/workflows.sh
|
||||
@python3 ./scripts/validate/manifest.py
|
||||
@python3 ./scripts/validate/xml_wellformed.py
|
||||
@python3 ./scripts/validate/workflows.py
|
||||
@echo "✓ Required validations passed"
|
||||
|
||||
## test: Run all tests
|
||||
@@ -96,18 +96,18 @@ phpcompat:
|
||||
## package: Create distribution package
|
||||
package:
|
||||
@echo "Creating distribution package..."
|
||||
@./scripts/release/package_extension.sh dist $(VERSION)
|
||||
@python3 ./scripts/release/package_extension.py dist $(VERSION)
|
||||
@echo "✓ Package created: dist/moko-cassiopeia-$(VERSION)-*.zip"
|
||||
|
||||
## smoke-test: Run smoke tests
|
||||
smoke-test:
|
||||
@echo "Running smoke tests..."
|
||||
@./scripts/run/smoke_test.sh
|
||||
@python3 ./scripts/run/smoke_test.py
|
||||
|
||||
## script-health: Check script health
|
||||
script-health:
|
||||
@echo "Checking script health..."
|
||||
@./scripts/run/script_health.sh
|
||||
@python3 ./scripts/run/script_health.py
|
||||
|
||||
## version-check: Display current version information
|
||||
version-check:
|
||||
@@ -119,7 +119,7 @@ version-check:
|
||||
## fix-permissions: Fix script executable permissions
|
||||
fix-permissions:
|
||||
@echo "Fixing script permissions..."
|
||||
@find scripts -type f -name "*.sh" -exec chmod +x {} \;
|
||||
@find scripts -type f -name "*.py" -exec chmod +x {} \;
|
||||
@echo "✓ Permissions fixed"
|
||||
|
||||
## clean: Remove generated files and caches
|
||||
@@ -174,13 +174,10 @@ watch:
|
||||
## list-scripts: List all available scripts
|
||||
list-scripts:
|
||||
@echo "Available validation scripts:"
|
||||
@find scripts/validate -type f -name "*.sh" -exec basename {} \; | sort
|
||||
@find scripts/validate -type f -name "*.py" -exec basename {} \; | sort
|
||||
@echo ""
|
||||
@echo "Available fix scripts:"
|
||||
@find scripts/fix -type f -name "*.sh" -exec basename {} \; | sort
|
||||
@echo ""
|
||||
@echo "Available run scripts (bash):"
|
||||
@find scripts/run -type f -name "*.sh" -exec basename {} \; | sort
|
||||
@find scripts/fix -type f -name "*.py" -exec basename {} \; | sort
|
||||
@echo ""
|
||||
@echo "Available run scripts (python):"
|
||||
@find scripts/run -type f -name "*.py" -exec basename {} \; | sort
|
||||
|
||||
0
scripts/validate/no_secrets.py
Normal file → Executable file
169
scripts/validate/paths.py
Executable file
@@ -0,0 +1,169 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Detect Windows-style path separators (backslashes).
|
||||
|
||||
Copyright (C) 2025 Moko Consulting <hello@mokoconsulting.tech>
|
||||
|
||||
This file is part of a Moko Consulting project.
|
||||
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program (./LICENSE.md).
|
||||
|
||||
FILE INFORMATION
|
||||
DEFGROUP: Script.Validate
|
||||
INGROUP: Path.Normalization
|
||||
REPO: https://github.com/mokoconsulting-tech/moko-cassiopeia
|
||||
PATH: /scripts/validate/paths.py
|
||||
VERSION: 01.00.00
|
||||
BRIEF: Detect Windows-style path separators (backslashes)
|
||||
NOTE: Ensures cross-platform path compatibility
|
||||
"""
|
||||
|
||||
import mimetypes
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from typing import List, Tuple, Dict
|
||||
|
||||
# Add lib directory to path
|
||||
sys.path.insert(0, str(Path(__file__).parent.parent / "lib"))
|
||||
|
||||
try:
|
||||
import common
|
||||
except ImportError:
|
||||
print("ERROR: Cannot import required libraries", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def get_tracked_files() -> List[str]:
|
||||
"""
|
||||
Get list of files tracked by git.
|
||||
|
||||
Returns:
|
||||
List of file paths
|
||||
"""
|
||||
try:
|
||||
result = common.run_command(
|
||||
["git", "ls-files", "-z"],
|
||||
capture_output=True,
|
||||
check=True
|
||||
)
|
||||
files = [f for f in result.stdout.split('\0') if f.strip()]
|
||||
return files
|
||||
except subprocess.CalledProcessError:
|
||||
return []
|
||||
|
||||
|
||||
def is_binary_file(filepath: str) -> bool:
|
||||
"""
|
||||
Check if a file is likely binary.
|
||||
|
||||
Args:
|
||||
filepath: Path to file
|
||||
|
||||
Returns:
|
||||
True if likely binary
|
||||
"""
|
||||
# Check mime type
|
||||
mime_type, _ = mimetypes.guess_type(filepath)
|
||||
if mime_type and mime_type.startswith(('application/', 'audio/', 'image/', 'video/')):
|
||||
return True
|
||||
|
||||
# Check for null bytes (heuristic for binary files)
|
||||
try:
|
||||
with open(filepath, 'rb') as f:
|
||||
chunk = f.read(1024)
|
||||
if b'\x00' in chunk:
|
||||
return True
|
||||
except Exception:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def find_backslashes_in_file(filepath: str) -> List[Tuple[int, str]]:
|
||||
"""
|
||||
Find lines with backslashes in a file.
|
||||
|
||||
Args:
|
||||
filepath: Path to file
|
||||
|
||||
Returns:
|
||||
List of (line_number, line_content) tuples
|
||||
"""
|
||||
backslashes = []
|
||||
|
||||
try:
|
||||
with open(filepath, 'r', encoding='utf-8', errors='ignore') as f:
|
||||
for line_num, line in enumerate(f, 1):
|
||||
if '\\' in line:
|
||||
backslashes.append((line_num, line.rstrip()))
|
||||
except Exception as e:
|
||||
common.log_warn(f"Could not read {filepath}: {e}")
|
||||
|
||||
return backslashes
|
||||
|
||||
|
||||
def main() -> int:
|
||||
"""Main entry point."""
|
||||
tracked_files = get_tracked_files()
|
||||
|
||||
if not tracked_files:
|
||||
print("No files to check")
|
||||
return 0
|
||||
|
||||
hits: Dict[str, List[Tuple[int, str]]] = {}
|
||||
|
||||
for filepath in tracked_files:
|
||||
# Skip binary files
|
||||
if is_binary_file(filepath):
|
||||
continue
|
||||
|
||||
# Find backslashes
|
||||
backslashes = find_backslashes_in_file(filepath)
|
||||
if backslashes:
|
||||
hits[filepath] = backslashes
|
||||
|
||||
if hits:
|
||||
print("ERROR: Windows-style path literals detected", file=sys.stderr)
|
||||
print("", file=sys.stderr)
|
||||
print(f"Found backslashes in {len(hits)} file(s):", file=sys.stderr)
|
||||
|
||||
for filepath, lines in hits.items():
|
||||
print("", file=sys.stderr)
|
||||
print(f" File: {filepath}", file=sys.stderr)
|
||||
print(" Lines with backslashes:", file=sys.stderr)
|
||||
|
||||
# Show first 5 lines
|
||||
for line_num, line_content in lines[:5]:
|
||||
print(f" {line_num}: {line_content[:80]}", file=sys.stderr)
|
||||
|
||||
if len(lines) > 5:
|
||||
print(f" ... and {len(lines) - 5} more", file=sys.stderr)
|
||||
|
||||
print("", file=sys.stderr)
|
||||
print("To fix:", file=sys.stderr)
|
||||
print(" 1. Run: python3 scripts/fix/paths.py", file=sys.stderr)
|
||||
print(" 2. Or manually replace backslashes (\\) with forward slashes (/)", file=sys.stderr)
|
||||
print(" 3. Ensure paths use POSIX separators for cross-platform compatibility", file=sys.stderr)
|
||||
print("", file=sys.stderr)
|
||||
return 2
|
||||
|
||||
print("paths: ok")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
0
scripts/validate/tabs.py
Normal file → Executable file
217
scripts/validate/workflows.py
Executable file
@@ -0,0 +1,217 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Validate GitHub Actions workflow files.
|
||||
|
||||
Copyright (C) 2025 Moko Consulting <hello@mokoconsulting.tech>
|
||||
|
||||
This file is part of a Moko Consulting project.
|
||||
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program (./LICENSE.md).
|
||||
|
||||
FILE INFORMATION
|
||||
DEFGROUP: Script.Validate
|
||||
INGROUP: CI.Validation
|
||||
REPO: https://github.com/mokoconsulting-tech/moko-cassiopeia
|
||||
PATH: /scripts/validate/workflows.py
|
||||
VERSION: 01.00.00
|
||||
BRIEF: Validate GitHub Actions workflow files
|
||||
NOTE: Checks YAML syntax, structure, and best practices
|
||||
"""
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from typing import List, Tuple
|
||||
Unused importImport of 'List' is not used. To fix an unused-import problem, remove the imported names that are not referenced anywhere in the file. This eliminates the static analysis warning without altering runtime behavior. In this case, the import ## Unused import
Import of 'List' is not used.<br>
Import of 'Tuple' is not used.
---
To fix an unused-import problem, remove the imported names that are not referenced anywhere in the file. This eliminates the static analysis warning without altering runtime behavior.</p>
<p>In this case, the import <code>from typing import List, Tuple</code> on line 36 is not used anywhere in the provided code. The best fix is to delete this line entirely. No other changes are needed: no new imports, no new functions, and no call-site updates. This change should be applied in <code>scripts/validate/workflows.py</code> at the import section near the top of the file, specifically removing line 36 and leaving the <code>import sys</code> and <code>from pathlib import Path</code> lines intact.
Import of 'List' is not used. Import of 'List' is not used.
Import of 'Tuple' is not used.
|
||||
|
||||
# Add lib directory to path
|
||||
sys.path.insert(0, str(Path(__file__).parent.parent / "lib"))
|
||||
|
||||
try:
|
||||
import common
|
||||
except ImportError:
|
||||
print("ERROR: Cannot import required libraries", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def validate_yaml_syntax(filepath: Path) -> bool:
|
||||
"""
|
||||
Validate YAML syntax of a workflow file.
|
||||
|
||||
Args:
|
||||
filepath: Path to workflow file
|
||||
|
||||
Returns:
|
||||
True if valid
|
||||
"""
|
||||
try:
|
||||
import yaml
|
||||
except ImportError:
|
||||
common.log_warn("PyYAML module not installed. Install with: pip3 install pyyaml")
|
||||
return True # Skip validation if yaml not available
|
||||
|
||||
try:
|
||||
with open(filepath, 'r', encoding='utf-8') as f:
|
||||
yaml.safe_load(f)
|
||||
print(f"✓ Valid YAML: {filepath.name}")
|
||||
return True
|
||||
except yaml.YAMLError as e:
|
||||
print(f"✗ YAML Error in {filepath.name}: {e}", file=sys.stderr)
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f"✗ Error reading {filepath.name}: {e}", file=sys.stderr)
|
||||
return False
|
||||
|
||||
|
||||
def check_no_tabs(filepath: Path) -> bool:
|
||||
"""
|
||||
Check that file contains no tab characters.
|
||||
|
||||
Args:
|
||||
filepath: Path to file
|
||||
|
||||
Returns:
|
||||
True if no tabs found
|
||||
"""
|
||||
try:
|
||||
with open(filepath, 'r', encoding='utf-8') as f:
|
||||
content = f.read()
|
||||
if '\t' in content:
|
||||
common.log_error(f"✗ File contains tab characters: {filepath.name}")
|
||||
return False
|
||||
except Exception as e:
|
||||
common.log_warn(f"Could not read {filepath}: {e}")
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def check_workflow_structure(filepath: Path) -> bool:
|
||||
"""
|
||||
Check workflow file structure for required keys.
|
||||
|
||||
Args:
|
||||
filepath: Path to workflow file
|
||||
|
||||
Returns:
|
||||
True if structure is valid
|
||||
"""
|
||||
errors = 0
|
||||
|
||||
try:
|
||||
with open(filepath, 'r', encoding='utf-8') as f:
|
||||
content = f.read()
|
||||
|
||||
# Check for required top-level keys
|
||||
if 'name:' not in content and not content.startswith('name:'):
|
||||
common.log_warn(f"Missing 'name:' in {filepath.name}")
|
||||
|
||||
if 'on:' not in content and not content.startswith('on:'):
|
||||
common.log_error(f"✗ Missing 'on:' trigger in {filepath.name}")
|
||||
errors += 1
|
||||
|
||||
if 'jobs:' not in content and not content.startswith('jobs:'):
|
||||
common.log_error(f"✗ Missing 'jobs:' in {filepath.name}")
|
||||
errors += 1
|
||||
|
||||
except Exception as e:
|
||||
common.log_error(f"Error reading {filepath}: {e}")
|
||||
return False
|
||||
|
||||
return errors == 0
|
||||
|
||||
|
||||
def validate_workflow_file(filepath: Path) -> bool:
|
||||
"""
|
||||
Validate a single workflow file.
|
||||
|
||||
Args:
|
||||
filepath: Path to workflow file
|
||||
|
||||
Returns:
|
||||
True if valid
|
||||
"""
|
||||
common.log_info(f"Validating: {filepath.name}")
|
||||
|
||||
errors = 0
|
||||
|
||||
# Check YAML syntax
|
||||
if not validate_yaml_syntax(filepath):
|
||||
errors += 1
|
||||
|
||||
# Check for tabs
|
||||
if not check_no_tabs(filepath):
|
||||
errors += 1
|
||||
|
||||
# Check structure
|
||||
if not check_workflow_structure(filepath):
|
||||
errors += 1
|
||||
|
||||
if errors == 0:
|
||||
common.log_info(f"✓ {filepath.name} passed all checks")
|
||||
return True
|
||||
else:
|
||||
common.log_error(f"✗ {filepath.name} failed {errors} check(s)")
|
||||
return False
|
||||
|
||||
|
||||
def main() -> int:
|
||||
"""Main entry point."""
|
||||
common.log_info("GitHub Actions Workflow Validation")
|
||||
common.log_info("===================================")
|
||||
print()
|
||||
|
||||
workflows_dir = Path(".github/workflows")
|
||||
|
||||
if not workflows_dir.is_dir():
|
||||
common.log_error(f"Workflows directory not found: {workflows_dir}")
|
||||
return 1
|
||||
|
||||
# Find all workflow files
|
||||
workflow_files = []
|
||||
for pattern in ["*.yml", "*.yaml"]:
|
||||
workflow_files.extend(workflows_dir.glob(pattern))
|
||||
|
||||
if not workflow_files:
|
||||
common.log_warn("No workflow files found")
|
||||
return 0
|
||||
|
||||
total = len(workflow_files)
|
||||
passed = 0
|
||||
failed = 0
|
||||
|
||||
for workflow in workflow_files:
|
||||
if validate_workflow_file(workflow):
|
||||
passed += 1
|
||||
else:
|
||||
failed += 1
|
||||
print()
|
||||
|
||||
common.log_info("===================================")
|
||||
common.log_info("Summary:")
|
||||
common.log_info(f" Total workflows: {total}")
|
||||
common.log_info(f" Passed: {passed}")
|
||||
common.log_info(f" Failed: {failed}")
|
||||
common.log_info("===================================")
|
||||
|
||||
if failed > 0:
|
||||
common.log_error("Workflow validation failed")
|
||||
return 1
|
||||
|
||||
common.log_info("All workflows validated successfully")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
The import of
extension_utilswill fail because this module is not included in the PR. The GitHub Actions workflow will fail at this step since the Python code cannot be executed without the required module.