Add dev tools and make build/release platform-aware (Joomla/Dolibarr)
Co-authored-by: jmiller-moko <230051081+jmiller-moko@users.noreply.github.com>
This commit is contained in:
63
.github/workflows/release_pipeline.yml
vendored
63
.github/workflows/release_pipeline.yml
vendored
@@ -643,7 +643,7 @@ else:
|
||||
echo "```"
|
||||
} >> "${GITHUB_STEP_SUMMARY}"
|
||||
|
||||
- name: Build Joomla ZIP (extension type aware, src-only archive)
|
||||
- name: Build Joomla/Dolibarr ZIP (platform-aware, src-only archive)
|
||||
id: build
|
||||
run: |
|
||||
set -euo pipefail
|
||||
@@ -657,44 +657,49 @@ else:
|
||||
DIST_DIR="${GITHUB_WORKSPACE}/dist"
|
||||
mkdir -p "${DIST_DIR}"
|
||||
|
||||
MANIFEST=""
|
||||
if [ -f "src/templateDetails.xml" ]; then
|
||||
MANIFEST="src/templateDetails.xml"
|
||||
elif find src -maxdepth 4 -type f -name 'templateDetails.xml' | head -n 1 | grep -q .; then
|
||||
MANIFEST="$(find src -maxdepth 4 -type f -name 'templateDetails.xml' | head -n 1)"
|
||||
elif find src -maxdepth 4 -type f -name 'pkg_*.xml' | head -n 1 | grep -q .; then
|
||||
MANIFEST="$(find src -maxdepth 4 -type f -name 'pkg_*.xml' | head -n 1)"
|
||||
elif find src -maxdepth 4 -type f -name 'com_*.xml' | head -n 1 | grep -q .; then
|
||||
MANIFEST="$(find src -maxdepth 4 -type f -name 'com_*.xml' | head -n 1)"
|
||||
elif find src -maxdepth 4 -type f -name 'mod_*.xml' | head -n 1 | grep -q .; then
|
||||
MANIFEST="$(find src -maxdepth 4 -type f -name 'mod_*.xml' | head -n 1)"
|
||||
elif find src -maxdepth 6 -type f -name 'plg_*.xml' | head -n 1 | grep -q .; then
|
||||
MANIFEST="$(find src -maxdepth 6 -type f -name 'plg_*.xml' | head -n 1)"
|
||||
else
|
||||
MANIFEST="$(grep -Rsl --include='*.xml' '<extension' src | head -n 1 || true)"
|
||||
fi
|
||||
|
||||
if [ -z "${MANIFEST}" ]; then
|
||||
echo "ERROR: No Joomla manifest XML found under src" >> "${GITHUB_STEP_SUMMARY}"
|
||||
# Detect platform and extension type using Python utility
|
||||
PLATFORM_INFO=$(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(f'{ext_info.platform.value}|{ext_info.extension_type}')
|
||||
else:
|
||||
sys.exit(1)
|
||||
")
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "ERROR: Could not detect extension platform and type" >> "${GITHUB_STEP_SUMMARY}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
PLATFORM="${PLATFORM_INFO%%|*}"
|
||||
EXT_TYPE="${PLATFORM_INFO##*|}"
|
||||
|
||||
EXT_TYPE="$(grep -Eo 'type="[^"]+"' "${MANIFEST}" | head -n 1 | cut -d '"' -f2 || true)"
|
||||
if [ -z "${EXT_TYPE}" ]; then
|
||||
EXT_TYPE="unknown"
|
||||
fi
|
||||
|
||||
ZIP="${REPO_NAME}-${VERSION}-${CHANNEL}-${EXT_TYPE}.zip"
|
||||
ZIP="${REPO_NAME}-${VERSION}-${CHANNEL}-${PLATFORM}-${EXT_TYPE}.zip"
|
||||
|
||||
# Create ZIP with development artifact exclusions
|
||||
zip -r -X "${DIST_DIR}/${ZIP}" src \
|
||||
-x "src/**/.git/**" \
|
||||
-x "src/**/.github/**" \
|
||||
-x "src/**/.DS_Store" \
|
||||
-x "src/**/__MACOSX/**"
|
||||
-x "src/**/__MACOSX/**" \
|
||||
-x "src/**/node_modules/**" \
|
||||
-x "src/**/vendor/**" \
|
||||
-x "src/**/tests/**" \
|
||||
-x "src/**/Tests/**" \
|
||||
-x "src/**/.phpstan.cache/**" \
|
||||
-x "src/**/.psalm/**" \
|
||||
-x "src/**/.rector/**" \
|
||||
-x "src/**/phpmd-cache/**" \
|
||||
-x "src/**/.php-cs-fixer.cache" \
|
||||
-x "src/**/.phplint-cache" \
|
||||
-x "src/**/*.log"
|
||||
|
||||
echo "zip_name=${ZIP}" >> "${GITHUB_OUTPUT}"
|
||||
echo "dist_dir=${DIST_DIR}" >> "${GITHUB_OUTPUT}"
|
||||
echo "manifest=${MANIFEST}" >> "${GITHUB_OUTPUT}"
|
||||
echo "platform=${PLATFORM}" >> "${GITHUB_OUTPUT}"
|
||||
echo "ext_type=${EXT_TYPE}" >> "${GITHUB_OUTPUT}"
|
||||
|
||||
ZIP_BYTES="$(stat -c%s "${DIST_DIR}/${ZIP}")"
|
||||
@@ -702,7 +707,7 @@ else:
|
||||
{
|
||||
echo "### Build report"
|
||||
echo "```json"
|
||||
echo "{\"repository\":\"${GITHUB_REPOSITORY}\",\"workflow\":\"${GITHUB_WORKFLOW}\",\"job\":\"${GITHUB_JOB}\",\"run/id\":${GITHUB_RUN_ID},\"run/number\":${GITHUB_RUN_NUMBER},\"run/attempt\":${GITHUB_RUN_ATTEMPT},\"run/url\":\"${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}\",\"actor\":\"${GITHUB_ACTOR}\",\"sha\":\"${GITHUB_SHA}\",\"archive_policy\":\"src_only\",\"manifest\":\"${MANIFEST}\",\"extension_type\":\"${EXT_TYPE}\",\"zip\":\"${DIST_DIR}/${ZIP}\",\"zip_bytes\":${ZIP_BYTES}}"
|
||||
echo "{\"repository\":\"${GITHUB_REPOSITORY}\",\"workflow\":\"${GITHUB_WORKFLOW}\",\"job\":\"${GITHUB_JOB}\",\"run/id\":${GITHUB_RUN_ID},\"run/number\":${GITHUB_RUN_NUMBER},\"run/attempt\":${GITHUB_RUN_ATTEMPT},\"run/url\":\"${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}\",\"actor\":\"${GITHUB_ACTOR}\",\"sha\":\"${GITHUB_SHA}\",\"archive_policy\":\"src_only\",\"platform\":\"${PLATFORM}\",\"extension_type\":\"${EXT_TYPE}\",\"zip\":\"${DIST_DIR}/${ZIP}\",\"zip_bytes\":${ZIP_BYTES}}"
|
||||
echo "```"
|
||||
} >> "${GITHUB_STEP_SUMMARY}"
|
||||
|
||||
|
||||
12
.gitignore
vendored
12
.gitignore
vendored
@@ -793,6 +793,14 @@ package-lock.json
|
||||
.phpunit.result.cache
|
||||
codeception.phar
|
||||
|
||||
# Development tool artifacts
|
||||
.phpstan.cache
|
||||
.psalm/
|
||||
.rector/
|
||||
phpmd-cache/
|
||||
.php-cs-fixer.cache
|
||||
.phplint-cache
|
||||
|
||||
# Python
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
@@ -804,8 +812,8 @@ develop-eggs/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
/lib/
|
||||
/lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
|
||||
42
Makefile
42
Makefile
@@ -26,8 +26,12 @@ install:
|
||||
@command -v composer >/dev/null 2>&1 || { echo "Error: composer not found. Please install composer first."; exit 1; }
|
||||
composer global require "squizlabs/php_codesniffer:^3.0" --with-all-dependencies
|
||||
composer global require "phpstan/phpstan:^1.0" --with-all-dependencies
|
||||
composer global require "phpstan/extension-installer:^1.0" --with-all-dependencies
|
||||
composer global require "phpcompatibility/php-compatibility:^9.0" --with-all-dependencies
|
||||
composer global require "codeception/codeception" --with-all-dependencies
|
||||
composer global require "vimeo/psalm:^5.0" --with-all-dependencies
|
||||
composer global require "phpmd/phpmd:^2.0" --with-all-dependencies
|
||||
composer global require "friendsofphp/php-cs-fixer:^3.0" --with-all-dependencies
|
||||
phpcs --config-set installed_paths ~/.composer/vendor/phpcompatibility/php-compatibility
|
||||
@echo "✓ Dependencies installed"
|
||||
|
||||
@@ -93,6 +97,38 @@ phpcompat:
|
||||
@command -v phpcs >/dev/null 2>&1 || { echo "Error: phpcs not found. Run 'make install' first."; exit 1; }
|
||||
phpcs --standard=PHPCompatibility --runtime-set testVersion 8.0- src/
|
||||
|
||||
## psalm: Run Psalm static analysis
|
||||
psalm:
|
||||
@echo "Running Psalm static analysis..."
|
||||
@command -v psalm >/dev/null 2>&1 || { echo "Error: psalm not found. Run 'make install' first."; exit 1; }
|
||||
psalm --show-info=false
|
||||
|
||||
## phpmd: Run PHP Mess Detector
|
||||
phpmd:
|
||||
@echo "Running PHP Mess Detector..."
|
||||
@command -v phpmd >/dev/null 2>&1 || { echo "Error: phpmd not found. Run 'make install' first."; exit 1; }
|
||||
phpmd src/ text cleancode,codesize,controversial,design,naming,unusedcode
|
||||
|
||||
## php-cs-fixer: Run PHP-CS-Fixer
|
||||
php-cs-fixer:
|
||||
@echo "Running PHP-CS-Fixer..."
|
||||
@command -v php-cs-fixer >/dev/null 2>&1 || { echo "Error: php-cs-fixer not found. Run 'make install' first."; exit 1; }
|
||||
php-cs-fixer fix --dry-run --diff src/
|
||||
|
||||
## php-cs-fixer-fix: Auto-fix with PHP-CS-Fixer
|
||||
php-cs-fixer-fix:
|
||||
@echo "Auto-fixing with PHP-CS-Fixer..."
|
||||
@command -v php-cs-fixer >/dev/null 2>&1 || { echo "Error: php-cs-fixer not found. Run 'make install' first."; exit 1; }
|
||||
php-cs-fixer fix src/
|
||||
|
||||
## quality-extended: Run extended quality checks (includes psalm, phpmd)
|
||||
quality-extended:
|
||||
@echo "Running extended code quality checks..."
|
||||
@$(MAKE) quality
|
||||
@$(MAKE) psalm
|
||||
@$(MAKE) phpmd
|
||||
@echo "✓ All quality checks passed"
|
||||
|
||||
## package: Create distribution package
|
||||
package:
|
||||
@echo "Creating distribution package..."
|
||||
@@ -128,6 +164,12 @@ clean:
|
||||
@rm -rf dist/
|
||||
@rm -rf tests/_output/
|
||||
@rm -rf .phpunit.cache/
|
||||
@rm -rf .phpstan.cache/
|
||||
@rm -rf .psalm/
|
||||
@rm -rf .rector/
|
||||
@rm -rf phpmd-cache/
|
||||
@find . -type f -name ".php-cs-fixer.cache" -delete
|
||||
@find . -type f -name ".phplint-cache" -delete
|
||||
@find . -type f -name "*.log" -delete
|
||||
@find . -type f -name ".DS_Store" -delete
|
||||
@echo "✓ Cleaned"
|
||||
|
||||
349
scripts/lib/extension_utils.py
Normal file
349
scripts/lib/extension_utils.py
Normal file
@@ -0,0 +1,349 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Extension utilities for Joomla and Dolibarr.
|
||||
|
||||
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.Library
|
||||
INGROUP: Extension.Utils
|
||||
REPO: https://github.com/mokoconsulting-tech/moko-cassiopeia
|
||||
PATH: /scripts/lib/extension_utils.py
|
||||
VERSION: 01.00.00
|
||||
BRIEF: Platform-aware extension utilities for Joomla and Dolibarr
|
||||
"""
|
||||
|
||||
import re
|
||||
import xml.etree.ElementTree as ET
|
||||
from dataclasses import dataclass
|
||||
from enum import Enum
|
||||
from pathlib import Path
|
||||
from typing import Optional, Union
|
||||
|
||||
|
||||
class Platform(Enum):
|
||||
"""Supported extension platforms."""
|
||||
JOOMLA = "joomla"
|
||||
DOLIBARR = "dolibarr"
|
||||
UNKNOWN = "unknown"
|
||||
|
||||
|
||||
@dataclass
|
||||
class ExtensionInfo:
|
||||
"""Extension information."""
|
||||
platform: Platform
|
||||
name: str
|
||||
version: str
|
||||
extension_type: str
|
||||
manifest_path: Path
|
||||
description: Optional[str] = None
|
||||
author: Optional[str] = None
|
||||
author_email: Optional[str] = None
|
||||
license: Optional[str] = None
|
||||
|
||||
|
||||
def detect_joomla_manifest(src_dir: Union[str, Path]) -> Optional[Path]:
|
||||
"""
|
||||
Detect Joomla manifest file.
|
||||
|
||||
Args:
|
||||
src_dir: Source directory
|
||||
|
||||
Returns:
|
||||
Path to manifest file or None
|
||||
"""
|
||||
src_path = Path(src_dir)
|
||||
|
||||
# Common Joomla manifest locations and patterns
|
||||
manifest_patterns = [
|
||||
"templateDetails.xml",
|
||||
"pkg_*.xml",
|
||||
"com_*.xml",
|
||||
"mod_*.xml",
|
||||
"plg_*.xml",
|
||||
]
|
||||
|
||||
# Search in src_dir and subdirectories (max depth 4)
|
||||
for pattern in manifest_patterns:
|
||||
# Direct match
|
||||
matches = list(src_path.glob(pattern))
|
||||
if matches:
|
||||
return matches[0]
|
||||
|
||||
# Search in subdirectories
|
||||
matches = list(src_path.glob(f"*/{pattern}"))
|
||||
if matches:
|
||||
return matches[0]
|
||||
|
||||
matches = list(src_path.glob(f"*/*/{pattern}"))
|
||||
if matches:
|
||||
return matches[0]
|
||||
|
||||
# Fallback: search for any XML with <extension tag
|
||||
for xml_file in src_path.rglob("*.xml"):
|
||||
if xml_file.name.startswith("."):
|
||||
continue
|
||||
try:
|
||||
tree = ET.parse(xml_file)
|
||||
root = tree.getroot()
|
||||
if root.tag == "extension":
|
||||
return xml_file
|
||||
except Exception:
|
||||
continue
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def detect_dolibarr_manifest(src_dir: Union[str, Path]) -> Optional[Path]:
|
||||
"""
|
||||
Detect Dolibarr module descriptor file.
|
||||
|
||||
Args:
|
||||
src_dir: Source directory
|
||||
|
||||
Returns:
|
||||
Path to descriptor file or None
|
||||
"""
|
||||
src_path = Path(src_dir)
|
||||
|
||||
# Dolibarr module descriptors follow pattern: core/modules/modMyModule.class.php
|
||||
descriptor_patterns = [
|
||||
"core/modules/mod*.class.php",
|
||||
"*/modules/mod*.class.php",
|
||||
"mod*.class.php",
|
||||
]
|
||||
|
||||
for pattern in descriptor_patterns:
|
||||
matches = list(src_path.glob(pattern))
|
||||
if matches:
|
||||
# Verify it's actually a Dolibarr module descriptor
|
||||
for match in matches:
|
||||
try:
|
||||
content = match.read_text(encoding="utf-8")
|
||||
if "extends DolibarrModules" in content or "class Mod" in content:
|
||||
return match
|
||||
except Exception:
|
||||
continue
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def parse_joomla_manifest(manifest_path: Path) -> Optional[ExtensionInfo]:
|
||||
"""
|
||||
Parse Joomla manifest XML.
|
||||
|
||||
Args:
|
||||
manifest_path: Path to manifest file
|
||||
|
||||
Returns:
|
||||
ExtensionInfo or None
|
||||
"""
|
||||
try:
|
||||
tree = ET.parse(manifest_path)
|
||||
root = tree.getroot()
|
||||
|
||||
if root.tag != "extension":
|
||||
return None
|
||||
|
||||
# Get extension type
|
||||
ext_type = root.get("type", "unknown")
|
||||
|
||||
# Get name
|
||||
name_elem = root.find("name")
|
||||
name = name_elem.text if name_elem is not None else "unknown"
|
||||
|
||||
# Get version
|
||||
version_elem = root.find("version")
|
||||
version = version_elem.text if version_elem is not None else "0.0.0"
|
||||
|
||||
# Get description
|
||||
desc_elem = root.find("description")
|
||||
description = desc_elem.text if desc_elem is not None else None
|
||||
|
||||
# Get author
|
||||
author_elem = root.find("author")
|
||||
author = author_elem.text if author_elem is not None else None
|
||||
|
||||
# Get author email
|
||||
author_email_elem = root.find("authorEmail")
|
||||
author_email = author_email_elem.text if author_email_elem is not None else None
|
||||
|
||||
# Get license
|
||||
license_elem = root.find("license")
|
||||
license_text = license_elem.text if license_elem is not None else None
|
||||
|
||||
return ExtensionInfo(
|
||||
platform=Platform.JOOMLA,
|
||||
name=name,
|
||||
version=version,
|
||||
extension_type=ext_type,
|
||||
manifest_path=manifest_path,
|
||||
description=description,
|
||||
author=author,
|
||||
author_email=author_email,
|
||||
license=license_text
|
||||
)
|
||||
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
|
||||
def parse_dolibarr_descriptor(descriptor_path: Path) -> Optional[ExtensionInfo]:
|
||||
"""
|
||||
Parse Dolibarr module descriptor PHP file.
|
||||
|
||||
Args:
|
||||
descriptor_path: Path to descriptor file
|
||||
|
||||
Returns:
|
||||
ExtensionInfo or None
|
||||
"""
|
||||
try:
|
||||
content = descriptor_path.read_text(encoding="utf-8")
|
||||
|
||||
# Extract module name from class name
|
||||
name_match = re.search(r'class\s+(Mod\w+)', content)
|
||||
name = name_match.group(1) if name_match else "unknown"
|
||||
|
||||
# Extract version
|
||||
version_match = re.search(r'\$this->version\s*=\s*[\'"]([^\'"]+)[\'"]', content)
|
||||
version = version_match.group(1) if version_match else "0.0.0"
|
||||
|
||||
# Extract description
|
||||
desc_match = re.search(r'\$this->description\s*=\s*[\'"]([^\'"]+)[\'"]', content)
|
||||
description = desc_match.group(1) if desc_match else None
|
||||
|
||||
# Extract author
|
||||
author_match = re.search(r'\$this->editor_name\s*=\s*[\'"]([^\'"]+)[\'"]', content)
|
||||
author = author_match.group(1) if author_match else None
|
||||
|
||||
return ExtensionInfo(
|
||||
platform=Platform.DOLIBARR,
|
||||
name=name,
|
||||
version=version,
|
||||
extension_type="module",
|
||||
manifest_path=descriptor_path,
|
||||
description=description,
|
||||
author=author,
|
||||
author_email=None,
|
||||
license=None
|
||||
)
|
||||
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
|
||||
def get_extension_info(src_dir: Union[str, Path]) -> Optional[ExtensionInfo]:
|
||||
"""
|
||||
Detect and parse extension information from source directory.
|
||||
Supports both Joomla and Dolibarr platforms.
|
||||
|
||||
Args:
|
||||
src_dir: Source directory containing extension files
|
||||
|
||||
Returns:
|
||||
ExtensionInfo or None if not detected
|
||||
"""
|
||||
src_path = Path(src_dir)
|
||||
|
||||
if not src_path.is_dir():
|
||||
return None
|
||||
|
||||
# Try Joomla first
|
||||
joomla_manifest = detect_joomla_manifest(src_path)
|
||||
if joomla_manifest:
|
||||
ext_info = parse_joomla_manifest(joomla_manifest)
|
||||
if ext_info:
|
||||
return ext_info
|
||||
|
||||
# Try Dolibarr
|
||||
dolibarr_descriptor = detect_dolibarr_manifest(src_path)
|
||||
if dolibarr_descriptor:
|
||||
ext_info = parse_dolibarr_descriptor(dolibarr_descriptor)
|
||||
if ext_info:
|
||||
return ext_info
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def is_joomla_extension(src_dir: Union[str, Path]) -> bool:
|
||||
"""
|
||||
Check if directory contains a Joomla extension.
|
||||
|
||||
Args:
|
||||
src_dir: Source directory
|
||||
|
||||
Returns:
|
||||
True if Joomla extension detected
|
||||
"""
|
||||
ext_info = get_extension_info(src_dir)
|
||||
return ext_info is not None and ext_info.platform == Platform.JOOMLA
|
||||
|
||||
|
||||
def is_dolibarr_extension(src_dir: Union[str, Path]) -> bool:
|
||||
"""
|
||||
Check if directory contains a Dolibarr module.
|
||||
|
||||
Args:
|
||||
src_dir: Source directory
|
||||
|
||||
Returns:
|
||||
True if Dolibarr module detected
|
||||
"""
|
||||
ext_info = get_extension_info(src_dir)
|
||||
return ext_info is not None and ext_info.platform == Platform.DOLIBARR
|
||||
|
||||
|
||||
def main() -> None:
|
||||
"""Test the extension utilities."""
|
||||
import sys
|
||||
sys.path.insert(0, str(Path(__file__).parent))
|
||||
import common
|
||||
|
||||
common.log_section("Testing Extension Utilities")
|
||||
|
||||
# Test with current directory's src
|
||||
repo_root = common.repo_root()
|
||||
src_dir = repo_root / "src"
|
||||
|
||||
if not src_dir.is_dir():
|
||||
common.log_warn(f"Source directory not found: {src_dir}")
|
||||
return
|
||||
|
||||
ext_info = get_extension_info(src_dir)
|
||||
|
||||
if ext_info:
|
||||
common.log_success("Extension detected!")
|
||||
common.log_kv("Platform", ext_info.platform.value.upper())
|
||||
common.log_kv("Name", ext_info.name)
|
||||
common.log_kv("Version", ext_info.version)
|
||||
common.log_kv("Type", ext_info.extension_type)
|
||||
common.log_kv("Manifest", str(ext_info.manifest_path))
|
||||
if ext_info.description:
|
||||
common.log_kv("Description", ext_info.description[:60] + "...")
|
||||
if ext_info.author:
|
||||
common.log_kv("Author", ext_info.author)
|
||||
else:
|
||||
common.log_error("No extension detected")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -62,6 +62,9 @@ EXCLUDE_PATTERNS = {
|
||||
# Documentation (optional, can be included)
|
||||
# Build artifacts
|
||||
"dist", "build", ".phpunit.cache",
|
||||
# Development tool caches and artifacts
|
||||
".phpstan.cache", ".psalm", ".rector",
|
||||
"phpmd-cache", ".php-cs-fixer.cache", ".phplint-cache",
|
||||
# OS files
|
||||
".DS_Store", "Thumbs.db",
|
||||
# Logs
|
||||
@@ -78,10 +81,11 @@ EXCLUDE_PATTERNS = {
|
||||
"composer.json", "composer.lock",
|
||||
"package.json", "package-lock.json",
|
||||
"phpunit.xml", "phpstan.neon", "phpcs.xml",
|
||||
"codeception.yml",
|
||||
"codeception.yml", "psalm.xml", ".php-cs-fixer.php",
|
||||
# Others
|
||||
"README.md", "CHANGELOG.md", "CONTRIBUTING.md",
|
||||
"CODE_OF_CONDUCT.md", "SECURITY.md", "GOVERNANCE.md",
|
||||
"Makefile",
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user