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
149 changes: 149 additions & 0 deletions .codacy/cli.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
#!/usr/bin/env bash


set -e +o pipefail

# Set up paths first
bin_name="codacy-cli-v2"

# Determine OS-specific paths
os_name=$(uname)
arch=$(uname -m)

case "$arch" in
"x86_64")
arch="amd64"
;;
"x86")
arch="386"
;;
"aarch64"|"arm64")
arch="arm64"
;;
esac

if [ -z "$CODACY_CLI_V2_TMP_FOLDER" ]; then
if [ "$(uname)" = "Linux" ]; then
CODACY_CLI_V2_TMP_FOLDER="$HOME/.cache/codacy/codacy-cli-v2"
elif [ "$(uname)" = "Darwin" ]; then
CODACY_CLI_V2_TMP_FOLDER="$HOME/Library/Caches/Codacy/codacy-cli-v2"
else
CODACY_CLI_V2_TMP_FOLDER=".codacy-cli-v2"
fi
fi

version_file="$CODACY_CLI_V2_TMP_FOLDER/version.yaml"


get_version_from_yaml() {
if [ -f "$version_file" ]; then
local version=$(grep -o 'version: *"[^"]*"' "$version_file" | cut -d'"' -f2)
if [ -n "$version" ]; then
echo "$version"
return 0
fi
fi
return 1
}

get_latest_version() {
local response
if [ -n "$GH_TOKEN" ]; then
response=$(curl -Lq --header "Authorization: Bearer $GH_TOKEN" "https://api.github.com/repos/codacy/codacy-cli-v2/releases/latest" 2>/dev/null)
else
response=$(curl -Lq "https://api.github.com/repos/codacy/codacy-cli-v2/releases/latest" 2>/dev/null)
fi

handle_rate_limit "$response"
local version=$(echo "$response" | grep -m 1 tag_name | cut -d'"' -f4)
echo "$version"
}

handle_rate_limit() {
local response="$1"
if echo "$response" | grep -q "API rate limit exceeded"; then
fatal "Error: GitHub API rate limit exceeded. Please try again later"
Copy link

Copilot AI Dec 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The script calls a fatal function on lines 65, 78, and 142, but this function is never defined. This will cause the script to fail with "fatal: command not found" when these error conditions are encountered.

Consider adding a fatal function definition at the beginning of the script:

fatal() {
    echo "Error: $1" >&2
    exit 1
}

Copilot uses AI. Check for mistakes.
fi
}

download_file() {
local url="$1"

echo "Downloading from URL: ${url}"
if command -v curl > /dev/null 2>&1; then
curl -# -LS "$url" -O
elif command -v wget > /dev/null 2>&1; then
wget "$url"
else
fatal "Error: Could not find curl or wget, please install one."
fi
}

download() {
local url="$1"
local output_folder="$2"

( cd "$output_folder" && download_file "$url" )
}

download_cli() {
# OS name lower case
suffix=$(echo "$os_name" | tr '[:upper:]' '[:lower:]')

local bin_folder="$1"
local bin_path="$2"
local version="$3"

if [ ! -f "$bin_path" ]; then
echo "📥 Downloading CLI version $version..."

remote_file="codacy-cli-v2_${version}_${suffix}_${arch}.tar.gz"
url="https://github.com/codacy/codacy-cli-v2/releases/download/${version}/${remote_file}"

download "$url" "$bin_folder"
tar xzfv "${bin_folder}/${remote_file}" -C "${bin_folder}"
fi
}

# Warn if CODACY_CLI_V2_VERSION is set and update is requested
if [ -n "$CODACY_CLI_V2_VERSION" ] && [ "$1" = "update" ]; then
echo "⚠️ Warning: Performing update with forced version $CODACY_CLI_V2_VERSION"
echo " Unset CODACY_CLI_V2_VERSION to use the latest version"
fi

# Ensure version.yaml exists and is up to date
if [ ! -f "$version_file" ] || [ "$1" = "update" ]; then
echo "ℹ️ Fetching latest version..."
version=$(get_latest_version)
mkdir -p "$CODACY_CLI_V2_TMP_FOLDER"
echo "version: \"$version\"" > "$version_file"
fi

# Set the version to use
if [ -n "$CODACY_CLI_V2_VERSION" ]; then
version="$CODACY_CLI_V2_VERSION"
else
version=$(get_version_from_yaml)
fi


# Set up version-specific paths
bin_folder="${CODACY_CLI_V2_TMP_FOLDER}/${version}"

mkdir -p "$bin_folder"
bin_path="$bin_folder"/"$bin_name"

# Download the tool if not already installed
download_cli "$bin_folder" "$bin_path" "$version"
chmod +x "$bin_path"

run_command="$bin_path"
if [ -z "$run_command" ]; then
fatal "Codacy cli v2 binary could not be found."
fi

if [ "$#" -eq 1 ] && [ "$1" = "download" ]; then
echo "Codacy cli v2 download succeeded"
else
eval "$run_command $*"
Copy link

Copilot AI Dec 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using eval "$run_command $*" to invoke the Codacy CLI builds a shell command string from CLI arguments, which allows shell metacharacters in those arguments to inject additional commands. For example, calling the script with an argument like "analyze; rm -rf /" would cause rm -rf / to be executed as a separate shell command. Replace this with a direct invocation such as "$run_command" "$@" so arguments are passed as argv elements instead of being re-parsed by the shell.

Suggested change
eval "$run_command $*"
"$run_command" "$@"

Copilot uses AI. Check for mistakes.
fi
10 changes: 10 additions & 0 deletions .codacy/codacy.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
runtimes:
- java@17.0.10
- node@22.2.0
- python@3.11.11
tools:
- eslint@8.57.0
- lizard@1.17.31
- pmd@6.55.0
- semgrep@1.78.0
- trivy@0.66.0
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,7 @@ build/
# Miscellaneous
.DS_Store
Thumbs.db


#Ignore insiders AI rules
.github/instructions/codacy.instructions.md
12 changes: 11 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,19 @@

All notable changes to the "magento-log-viewer" extension will be documented in this file.


## Next release

### [1.23.1] - 2025-12-08

- fix: Enhanced log level detection pattern to support both uppercase and lowercase formats (.WARN:, .warn:, .INFO:, .info: etc.)
- fix: WARN entries now properly appear in `*.log` file listing and categorization
- fix: Improved regex pattern matching from `\.(\w+):` to `\.([A-Za-z]+):` for more reliable log parsing
- fix: Resolved issue where certain log level formats were not being recognized during file analysis
- fix: Enhanced badge counting accuracy for all log level variations
- fix: Null safety improvements for status bar item to prevent potential crashes
- refactor: Removed unused functions and improved code
- refactor: Cleaned up method signatures by removing unnecessary parameters
- fix: Updated test interfaces to match current implementation

---

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "magento-log-viewer",
"displayName": "Magento Log Viewer",
"description": "A Visual Studio Code extension to view and manage Magento log files.",
"version": "1.23.0",
"version": "1.23.1",
"publisher": "MathiasElle",
"icon": "resources/logo.png",
"repository": {
Expand Down
2 changes: 1 addition & 1 deletion src/extension.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as vscode from 'vscode';
import { promptMagentoProjectSelection, showErrorMessage, activateExtension, isValidPath, deleteReportFile, clearFileContentCache, selectMagentoRootFolder, selectMagentoRootFolderDirect, getEffectiveMagentoRoot, selectMagentoRootFromSettings, autoCleanupOldLogFiles, stopPeriodicCleanup } from './helpers';
import { promptMagentoProjectSelection, showErrorMessage, activateExtension, isValidPath, deleteReportFile, clearFileContentCache, selectMagentoRootFolderDirect, getEffectiveMagentoRoot, selectMagentoRootFromSettings, autoCleanupOldLogFiles, stopPeriodicCleanup } from './helpers';
Copy link

Copilot AI Dec 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused import autoCleanupOldLogFiles.

Suggested change
import { promptMagentoProjectSelection, showErrorMessage, activateExtension, isValidPath, deleteReportFile, clearFileContentCache, selectMagentoRootFolderDirect, getEffectiveMagentoRoot, selectMagentoRootFromSettings, autoCleanupOldLogFiles, stopPeriodicCleanup } from './helpers';
import { promptMagentoProjectSelection, showErrorMessage, activateExtension, isValidPath, deleteReportFile, clearFileContentCache, selectMagentoRootFolderDirect, getEffectiveMagentoRoot, selectMagentoRootFromSettings, stopPeriodicCleanup } from './helpers';

Copilot uses AI. Check for mistakes.
import { LogItem, ReportViewerProvider } from './logViewer';
import { showUpdateNotification } from './updateNotifier';

Expand Down
28 changes: 3 additions & 25 deletions src/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -455,7 +455,9 @@ export function updateBadge(treeView: vscode.TreeView<unknown>, logViewerProvide
vscode.commands.executeCommand('setContext', 'magentoLogViewer.hasLogFiles', totalEntries > 0);

// Update status bar item
LogViewerProvider.statusBarItem.text = `Magento Log-Entries: ${totalEntries}`;
if (LogViewerProvider.statusBarItem) {
LogViewerProvider.statusBarItem.text = `Magento Log-Entries: ${totalEntries}`;
}
};

// Debounced event handler
Expand Down Expand Up @@ -497,30 +499,6 @@ function countFilesInDirectory(dir: string): number {
return count;
}

function getAllReportFiles(dir: string): LogItem[] {
if (!pathExists(dir)) {
return [];
}

const items: LogItem[] = [];
const files = fs.readdirSync(dir);

files.forEach(file => {
const filePath = path.join(dir, file);
if (fs.lstatSync(filePath).isDirectory()) {
items.push(...getAllReportFiles(filePath));
} else if (fs.lstatSync(filePath).isFile()) {
items.push(new LogItem(file, vscode.TreeItemCollapsibleState.None, {
command: 'magento-log-viewer.openFile',
title: 'Open Log File',
arguments: [filePath]
}));
}
});

return items;
}

// Checks if the given path is a valid directory.
export function isValidPath(filePath: string): boolean {
try {
Expand Down
25 changes: 13 additions & 12 deletions src/logViewer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { pathExists, pathExistsAsync, getLineCount, getIconForLogLevel, getLogIt
export class LogViewerProvider implements vscode.TreeDataProvider<LogItem>, vscode.Disposable {
private _onDidChangeTreeData: vscode.EventEmitter<LogItem | undefined | void> = new vscode.EventEmitter<LogItem | undefined | void>();
readonly onDidChangeTreeData: vscode.Event<LogItem | undefined | void> = this._onDidChangeTreeData.event;
public static statusBarItem: vscode.StatusBarItem;
public static statusBarItem: vscode.StatusBarItem | undefined;
private groupByMessage: boolean;
private disposables: vscode.Disposable[] = [];
private isInitialized: boolean = false;
Expand Down Expand Up @@ -243,7 +243,7 @@ export class LogViewerProvider implements vscode.TreeDataProvider<LogItem>, vsco
return normalizedDir === normalizedLogPath;
}

private getLogItems(dir: string, label: string): LogItem[] {
private getLogItems(dir: string): LogItem[] {
if (!pathExists(dir)) {
return [new LogItem(`No items found`, vscode.TreeItemCollapsibleState.None)];
}
Expand Down Expand Up @@ -303,14 +303,13 @@ export class LogViewerProvider implements vscode.TreeDataProvider<LogItem>, vsco
const groupedByType = new Map<string, { message: string, line: string, lineNumber: number }[]>();

console.log(`[DEBUG] Processing ${lines.length} lines for ${filePath}`);
let matchCount = 0;

lines.forEach((line, index) => {
const match = line.match(/\.(\w+):/);
// Enhanced regex to match both formats: .level: and .LEVEL:
const match = line.match(/\.([A-Za-z]+):/);
if (match) {
matchCount++;
const level = match[1].toUpperCase();
const message = line.replace(/^\[.*?\]\s*\.\w+:\s*/, '');
const message = line.replace(/^\[.*?\]\s*\.[A-Za-z]+:\s*/, '');

// Apply search filter
if (this.matchesSearchTerm(line) || this.matchesSearchTerm(message)) {
Expand Down Expand Up @@ -413,9 +412,9 @@ export class LogViewerProvider implements vscode.TreeDataProvider<LogItem>, vsco
if (fileContent) {
const lines = fileContent.split('\n');

// Only count valid log entries matching the expected pattern
// Only count valid log entries matching the expected pattern (enhanced for both formats)
lines.forEach(line => {
if (line.match(/\.(\w+):/)) { // The pattern for log entries
if (line.match(/\.([A-Za-z]+):/)) { // Updated pattern to match both .level: and .LEVEL:
logEntryCount++;
}
});
Expand All @@ -441,7 +440,9 @@ export class LogViewerProvider implements vscode.TreeDataProvider<LogItem>, vsco
const totalEntries = logFiles.reduce((count, file) => count + parseInt(file.description?.match(/\d+/)?.[0] || '0', 10), 0);

const searchInfo = this.searchTerm ? ` | Search: "${this.searchTerm}"` : '';
LogViewerProvider.statusBarItem.text = `Magento Log-Entries: ${totalEntries}${searchInfo}`;
if (LogViewerProvider.statusBarItem) {
LogViewerProvider.statusBarItem.text = `Magento Log-Entries: ${totalEntries}${searchInfo}`;
}
}

/**
Expand Down Expand Up @@ -522,7 +523,7 @@ export class LogViewerProvider implements vscode.TreeDataProvider<LogItem>, vsco
this._onDidChangeTreeData.dispose();
if (LogViewerProvider.statusBarItem) {
LogViewerProvider.statusBarItem.dispose();
LogViewerProvider.statusBarItem = null as any;
LogViewerProvider.statusBarItem = undefined;
}
// Clear regex cache to prevent memory leaks
this.cachedSearchRegex = null;
Expand Down Expand Up @@ -678,7 +679,7 @@ export class ReportViewerProvider implements vscode.TreeDataProvider<LogItem>, v
setTimeout(() => {
try {
const reportPath = path.join(this.workspaceRoot, 'var', 'report');
const reportItems = this.getLogItems(reportPath, 'Reports');
const reportItems = this.getLogItems(reportPath);
if (reportItems.length === 0) {
resolve([new LogItem('No report files found', vscode.TreeItemCollapsibleState.None)]);
} else {
Expand All @@ -693,7 +694,7 @@ export class ReportViewerProvider implements vscode.TreeDataProvider<LogItem>, v
}
}

private getLogItems(dir: string, label: string): LogItem[] {
private getLogItems(dir: string): LogItem[] {
const allItems = getLogItems(dir, parseReportTitle, getIconForReport);

// Apply search filter
Expand Down
4 changes: 2 additions & 2 deletions src/test/reportReader.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,14 +53,14 @@ suite('Report Reader Test Suite', () => {

// Interface for accessing private methods for testing
interface ReportViewerInternals {
getLogItems(dir: string, label: string): LogItem[];
getLogItems(dir: string): LogItem[];
}

// Access the provider's internal methods
const provider = reportProvider as unknown as ReportViewerInternals;

// Get report items from the directory
const reportItems = provider.getLogItems(tempDir, 'Reports');
const reportItems = provider.getLogItems(tempDir);

// Basic validation that reports were found
assert.ok(reportItems.length > 0, 'Should find report entries');
Expand Down