-
Notifications
You must be signed in to change notification settings - Fork 16
feat: copy to webgui repo script docs + wc build options #1285
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: copy to webgui repo script docs + wc build options #1285
Conversation
WalkthroughThis pull request introduces updates to configuration files and sync-related scripts. The Changes
Sequence Diagram(s)sequenceDiagram
participant U as User
participant A as App (sync-webgui-repo.js)
participant M as Menu
participant UI as UI Module
participant FO as FileOps Module
participant FS as FileSystem Module
U->>A: Start sync process
A->>M: Initialize and display menu
M->>U: Present action options
U->>M: Choose a synchronization action
M->>UI: Relay selected action
UI->>FO: Request file operations (e.g., load gitignore, diff files)
FO->>FS: Execute file system tasks (read/write, copy files)
FS-->>FO: Return results
FO-->>UI: Provide synchronization status
UI-->>M: Log operation feedback
M-->>A: Conclude action
A-->>U: Notify synchronization complete
Suggested reviewers
Poem
🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (7)
.gitignore (1)
104-106: Add a brief explanation of why these files are ignored.
Consider appending a quick comment describing the purpose of.sync-webgui-repo-*so that new contributors understand the rationale behind ignoring these script-related files.web/scripts/sync-webgui-repo.js (6)
1-24: Centralize error handling for missing dependencies.
The script gracefully imports multiple modules (fs,path, etc.) but does not handle cases where dependencies might be missing or incorrectly installed. Adding a quick dependency check or a user-friendly warning if a module import fails could improve reliability.
26-78: Consider switching to asynchronous methods.
All file operations are synchronous (e.g.,readFileSync,copyFileSync). While acceptable for a quick CLI tool, asynchronous methods could provide better scalability if the script grows in complexity or needs parallel processing.
80-117: Ensure cross-platform support for sound notifications.
TheUI.playSound()method only checks known paths for macOS, Linux, and Windows. If users have custom system setups or different file paths, consider allowing them to configure sound commands or handle missing commands gracefully.
119-239: Validate file paths before usage.
TheStateandFileOpsobjects read from and write to JSON files. If paths are invalid or corrupted JSON is encountered, consider gracefully handling errors and prompting users to fix the files or reinitialize them.
241-508: Evaluate build errors for partial successes.
When building web components inhandleWebComponentBuild(), the script treats the entire process as a single success/fail step. For complex builds, consider capturing partial successes or logging more granular build output to help diagnose smaller step failures.
610-630: Include automated tests.
TheApp.init()flow orchestrates user prompts and environment setup but lacks automated tests. Adding a few unit or integration tests (possibly mockingUIand file operations) would help ensure reliability for repeated usage.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro (Legacy)
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (4)
.gitignore(1 hunks)package.json(2 hunks)web/scripts/copy-to-webgui-repo.sh(0 hunks)web/scripts/sync-webgui-repo.js(1 hunks)
💤 Files with no reviewable changes (1)
- web/scripts/copy-to-webgui-repo.sh
⏰ Context from checks skipped due to timeout of 90000ms (4)
- GitHub Check: Build Web App
- GitHub Check: Test API
- GitHub Check: Build API
- GitHub Check: Cloudflare Pages
🔇 Additional comments (2)
package.json (2)
14-15: Ensure script clarity.
Adding"sync-webgui-repo": "node web/scripts/sync-webgui-repo.js"is good for future reference. If further arguments or environment variables are needed, consider updating the script interface to accommodate them.
37-40:Details
❓ Verification inconclusive
Check for security updates.
While these dependencies are helpful for file operations and user interactions, ensure they are kept up to date to avoid potential vulnerabilities.Please run a quick check on known vulnerabilities for these packages. For instance:
🏁 Script executed:
#!/bin/bash npm auditLength of output: 360
Security Audit Required: Missing Lockfile Detected
It appears that running the audit failed due to a missing lock file. Please create a lock file (usingnpm i --package-lock-only) and then re-runnpm auditto confirm that these dependencies have no known vulnerabilities.
- Ensure a package-lock file is present:
npm i --package-lock-only npm audit- Verify that the following dependencies are free from security issues:
@manypkg/clichalkdiffignore
115c53a to
bfdfb14
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (4)
web/scripts/sync-webgui-repo.js (4)
154-194: Consider enhancing diff visualization.The diff visualization works but could be improved for better clarity, especially for larger diffs.
Consider adding line numbers and limiting the context of diffs for very large files:
showDiff(apiFile, webguiFile) { const content1 = fs.readFileSync(apiFile, 'utf8'); const content2 = fs.readFileSync(webguiFile, 'utf8'); const differences = diff.createPatch( path.basename(apiFile), content2, content1, 'webgui version', 'api version' ); if (differences.split('\n').length > 5) { console.log('\nDiff for', chalk.cyan(path.basename(apiFile))); console.log(chalk.red('--- webgui:'), webguiFile); console.log(chalk.green('+++ api: '), apiFile); + // Limit large diffs to improve readability + const diffLines = differences.split('\n').slice(5); + const maxLines = 50; + const showTruncationMessage = diffLines.length > maxLines; + const linesToShow = showTruncationMessage ? diffLines.slice(0, maxLines) : diffLines; + + let lineCounter = 0; differences .split('\n') .slice(5) + .slice(0, maxLines) .forEach((line) => { - if (line.startsWith('+')) console.log(chalk.green(line)); - else if (line.startsWith('-')) console.log(chalk.red(line)); - else if (line.startsWith('@')) console.log(chalk.cyan(line)); - else console.log(line); + lineCounter++; + const lineNum = `${lineCounter}`.padStart(4); + if (line.startsWith('+')) console.log(`${lineNum} ${chalk.green(line)}`); + else if (line.startsWith('-')) console.log(`${lineNum} ${chalk.red(line)}`); + else if (line.startsWith('@')) console.log(`${lineNum} ${chalk.cyan(line)}`); + else console.log(`${lineNum} ${line}`); }); + + if (showTruncationMessage) { + console.log(chalk.yellow(`\n... (${diffLines.length - maxLines} more lines not shown)`)); + console.log(chalk.yellow(`Total diff size: ${diffLines.length} lines\n`)); + } return true; } return false; },
196-210: Enhance user experience in handleFileDiff with better prompts.The prompt message "What should I do fam?" is informal, which may be fine for a personal tool but could be improved for better clarity.
Consider improving the prompt with more detailed options:
async handleFileDiff(apiFile, webguiFile) { if (!this.showDiff(apiFile, webguiFile)) return 'identical'; const answer = await UI.question( - 'What should I do fam? (w=copy to webgui/a=copy to API/s=skip) ' + 'Action to take: (w=copy to webgui, a=copy to API, s=skip) ' ); switch (answer.toLowerCase()) { case 'w': return FileSystem.copyFile(apiFile, webguiFile) ? 'webgui' : 'error'; case 'a': return FileSystem.copyFile(webguiFile, apiFile) ? 'api' : 'error'; default: return 'skip'; } },
212-239: Enhance directory traversal with more explicit filtering.The traversal function has inline filtering logic that could be extracted to make the code more maintainable.
Consider refactoring to make the filtering logic more explicit:
walkDirectory(currentPath, baseDir, projectFiles, gitignoreRules) { const files = fs.readdirSync(currentPath); + // Helper function to determine if a file should be skipped + const shouldSkipFile = (file, relativePath) => { + if (file.startsWith('.')) return true; + if (relativePath.includes('test') || relativePath.includes('tests')) return true; + if (CONSTANTS.IGNORE_PATTERNS.some((pattern) => pattern.test(file))) return true; + if (gitignoreRules?.ignores(relativePath)) return true; + return false; + }; files.forEach((file) => { - if (file.startsWith('.')) return; - const fullPath = path.join(currentPath, file); const relativePath = path.relative(baseDir, fullPath); - if (relativePath.includes('test') || relativePath.includes('tests')) return; - if (CONSTANTS.IGNORE_PATTERNS.some((pattern) => pattern.test(file))) return; - if (gitignoreRules?.ignores(relativePath)) return; + if (shouldSkipFile(file, relativePath)) return; const lstat = fs.lstatSync(fullPath); if (lstat.isSymbolicLink()) return; const stats = fs.statSync(fullPath); if (stats.isDirectory()) { this.walkDirectory(fullPath, baseDir, projectFiles, gitignoreRules); } else { if (!projectFiles.has(file)) { projectFiles.set(file, []); } projectFiles.get(file).push(fullPath); } }); },
349-444: Improve error handling in findMissingPluginFiles function.The function contains a nested walkDir function with verbose logging but could benefit from more robust error handling.
Consider adding try/catch blocks around file operations within the walkDir function:
function walkDir(currentPath, baseDir) { if (!fs.existsSync(currentPath)) { UI.log(`Directory doesn't exist: ${currentPath}`, 'warning'); return; } UI.log(`Checking directory: ${path.relative(apiProjectDir, currentPath)}`, 'info'); fs.readdirSync(currentPath).forEach((file) => { if (file.startsWith('.')) { UI.log(`Skipping dot file/dir: ${file}`, 'skip'); return; } const fullPath = path.join(currentPath, file); const relativePath = path.relative(apiPluginsPath, fullPath); if (CONSTANTS.IGNORE_PATTERNS.some((pattern) => pattern.test(file))) { UI.log(`Skipping ignored pattern: ${file}`, 'skip'); return; } if (gitignore1?.ignores(relativePath)) { UI.log(`Skipping gitignored file: ${file}`, 'skip'); return; } + try { const lstat = fs.lstatSync(fullPath); if (lstat.isSymbolicLink()) { UI.log(`Skipping symlink: ${file}`, 'skip'); return; } const stats = fs.statSync(fullPath); if (stats.isDirectory()) { UI.log(`Found subdirectory: ${file}`, 'info'); walkDir(fullPath, baseDir); return; } if (ignoredFiles.includes(file)) { UI.log(`Skipping manually ignored file: ${file}`, 'skip'); return; } const webguiPath = path.join(webguiPluginsPath, relativePath); if (!fs.existsSync(webguiPath)) { UI.log(`Found new file: ${relativePath}`, 'new'); missingFiles.set(relativePath, { source: fullPath, destinationPath: webguiPath, relativePath, }); } else { UI.log(`File exists in both: ${relativePath}`, 'success'); } + } catch (err) { + UI.log(`Error processing file ${file}: ${err.message}`, 'error'); + } }); }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro (Legacy)
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (4)
.gitignore(1 hunks)package.json(2 hunks)web/scripts/copy-to-webgui-repo.sh(0 hunks)web/scripts/sync-webgui-repo.js(1 hunks)
💤 Files with no reviewable changes (1)
- web/scripts/copy-to-webgui-repo.sh
🚧 Files skipped from review as they are similar to previous changes (2)
- .gitignore
- package.json
⏰ Context from checks skipped due to timeout of 90000ms (4)
- GitHub Check: Build API
- GitHub Check: Build Web App
- GitHub Check: Test API
- GitHub Check: Cloudflare Pages
🔇 Additional comments (13)
web/scripts/sync-webgui-repo.js (13)
1-8: Necessary dependencies are properly imported.The script correctly imports all required Node.js core modules and external dependencies needed for file operations, diffing, and UI interactions.
9-24: Well-organized constants structure.The CONSTANTS object is well-structured with clear categorization of paths, ignore patterns, and directory locations. This makes the configuration easy to understand and maintain.
26-78: FileSystem module implements robust file operations.The module provides comprehensive file handling functionality with proper error handling. The
copyDirectorymethod correctly handles recursive copying of directories and files.
80-117: Good UI interaction implementation.The UI module effectively handles user interactions with proper promise-based question handling and visual feedback through icons. The cross-platform sound implementation is a nice touch for user experience.
119-152: Robust state management.The State module provides clean interfaces for persisting configuration and state between runs. The path normalization in
savePathsensures consistent path formats.
241-268: Good implementation of path setup.The
setupPathsfunction provides a good user experience by remembering previous paths and allowing users to confirm or change them.
269-298: Web component build process handles errors properly.The build process implementation correctly streams output and properly resolves/rejects the promise based on the exit code.
300-327: Web component sync handles old file cleanup properly.The sync process correctly removes the old directory before copying new files, which prevents orphaned files from previous builds.
329-347: findMatchingFiles effectively compares files across repositories.The function correctly identifies files that exist in both repositories by comparing filenames.
446-508: Good implementation of handleNewFiles with user interaction.The function provides an interactive workflow for handling new files with options to copy, ignore, or skip, and properly updates the state after handling files.
511-608: Menu implementation provides clear options for the user.The menu structure is clear and provides good error handling, ensuring the application doesn't crash when unexpected inputs occur.
610-623: App initialization module is well-structured.The App module cleanly encapsulates the initialization logic and properly handles errors.
625-629: Appropriate error handling for the main application.The script properly handles any uncaught errors in the main promise chain.
- checks for new missing plugin files - interactively handle new missing plugin files - checks for files to update between repos, displaying diff, with interaction to update in either direction - build and sync web components
…ction - Modified the menu display to keep showing until the user chooses to exit. - Enhanced error handling to ensure the application exits gracefully on errors. - Maintained existing functionality for handling new plugin files, syncing, and building web components.
- Revised user prompts for clarity in file copy operations. - Adjusted the switch cases to ensure correct file copy direction based on user input.
Per discussion w/ Eli removed the activation code debugger file from the repo. I kept it locally though, hence the `.gitignore` update. Which means I kept the helper method in the activation code extractor class so I could still use it. coderabbit also made a good suggestion in the webgui repo where I synced changes from this repo for web-component-extractor so I implemented and tested it. Included via [`ec29069` (#1287)](ec29069)
90a775e to
c78d1c2
Compare
|
This plugin has been deployed to Cloudflare R2 and is available for testing. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
🧹 Nitpick comments (4)
web/scripts/sync-webgui-repo.js (4)
1-8: Consider adding module documentation.Adding a brief module header comment explaining the purpose of this script would help other developers understand its functionality at a glance.
+/** + * Synchronizes files between API and WebGUI repositories + * Supports finding new plugin files, handling diffs, and syncing web components + */ const fs = require('fs'); const path = require('path'); const ignore = require('ignore');
165-194: Improve diff visualization with additional context.The diff function could be enhanced by providing more context about what's being compared and adding color indicators for clarity.
showDiff(apiFile, webguiFile) { const content1 = fs.readFileSync(apiFile, 'utf8'); const content2 = fs.readFileSync(webguiFile, 'utf8'); + // Add error handling for file reads + try { const differences = diff.createPatch( path.basename(apiFile), content2, content1, 'webgui version', 'api version' ); if (differences.split('\n').length > 5) { - console.log('\nDiff for', chalk.cyan(path.basename(apiFile))); + console.log('\n' + chalk.bold(`Diff for ${path.basename(apiFile)}`)); console.log(chalk.red('--- webgui:'), webguiFile); console.log(chalk.green('+++ api: '), apiFile); differences .split('\n') .slice(5) .forEach((line) => { - if (line.startsWith('+')) console.log(chalk.green(line)); - else if (line.startsWith('-')) console.log(chalk.red(line)); + if (line.startsWith('+')) console.log(chalk.green(line) + ' ← API'); + else if (line.startsWith('-')) console.log(chalk.red(line) + ' ← WebGUI'); else if (line.startsWith('@')) console.log(chalk.cyan(line)); else console.log(line); }); return true; } return false; + } catch (err) { + UI.log(`Error comparing files: ${err.message}`, 'error'); + return false; + } },
224-229: Optimize file stat operations.You're calling both
lstatSyncandstatSyncon the same file, which is inefficient. Consider consolidating these operations.- const lstat = fs.lstatSync(fullPath); - if (lstat.isSymbolicLink()) return; - - const stats = fs.statSync(fullPath); + // Get file stats with the withFileTypes option to determine type + const stats = fs.statSync(fullPath, { throwIfNoEntry: false }); + if (!stats) return; + + // Use fs.lstatSync only when checking for symbolic links + if (stats.isSymbolicLink?.() || fs.lstatSync(fullPath).isSymbolicLink()) return;
610-624: Add validation for project paths.The script doesn't verify if the paths provided by the user are valid repository roots. Add some basic validation to ensure they point to actual repositories.
const App = { async init() { try { const paths = await Features.setupPaths(); + + // Validate paths point to valid repositories + const validateRepo = (dir, name) => { + if (!fs.existsSync(dir)) { + throw new Error(`${name} directory doesn't exist: ${dir}`); + } + + // Check for common repository indicators + const hasGit = fs.existsSync(path.join(dir, '.git')); + const hasPackageJson = fs.existsSync(path.join(dir, 'package.json')); + + if (!hasGit && !hasPackageJson) { + UI.log(`Warning: ${name} directory might not be a valid repository.`, 'warning'); + return UI.confirm(`Continue with this path anyway?`); + } + return true; + }; + + // Validate both paths + const apiValid = await validateRepo(paths.apiProjectDir, 'API'); + const webguiValid = await validateRepo(paths.webguiProjectDir, 'WebGUI'); + + if (!apiValid || !webguiValid) { + throw new Error('Invalid repository paths. Please restart and provide valid paths.'); + } + global.apiProjectDir = paths.apiProjectDir; global.webguiProjectDir = paths.webguiProjectDir; await Menu.show();
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro (Legacy)
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (8)
.gitignore(1 hunks)package.json(2 hunks)plugin/source/dynamix.unraid.net/usr/local/emhttp/plugins/dynamix.my.servers/data/activation-data.php(0 hunks)plugin/source/dynamix.unraid.net/usr/local/emhttp/plugins/dynamix.my.servers/include/activation-code-extractor.php(0 hunks)plugin/source/dynamix.unraid.net/usr/local/emhttp/plugins/dynamix.my.servers/include/web-components-extractor.php(1 hunks)plugin/source/dynamix.unraid.net/usr/local/emhttp/plugins/dynamix.my.servers/scripts/activation_code_remove(0 hunks)web/scripts/copy-to-webgui-repo.sh(0 hunks)web/scripts/sync-webgui-repo.js(1 hunks)
💤 Files with no reviewable changes (4)
- plugin/source/dynamix.unraid.net/usr/local/emhttp/plugins/dynamix.my.servers/include/activation-code-extractor.php
- plugin/source/dynamix.unraid.net/usr/local/emhttp/plugins/dynamix.my.servers/scripts/activation_code_remove
- plugin/source/dynamix.unraid.net/usr/local/emhttp/plugins/dynamix.my.servers/data/activation-data.php
- web/scripts/copy-to-webgui-repo.sh
🚧 Files skipped from review as they are similar to previous changes (3)
- plugin/source/dynamix.unraid.net/usr/local/emhttp/plugins/dynamix.my.servers/include/web-components-extractor.php
- .gitignore
- package.json
⏰ Context from checks skipped due to timeout of 90000ms (3)
- GitHub Check: Build Web App
- GitHub Check: Build API
- GitHub Check: Cloudflare Pages
🔇 Additional comments (2)
web/scripts/sync-webgui-repo.js (2)
511-608: Consider adding a batch operation mode for file sync.The interactive design requires handling each file individually. For users with many files, a batch operation option would be more efficient.
const Menu = { async show() { while (true) { // Keep showing menu until exit try { console.log('\nWhat you trying to do fam?'); console.log('1. Find new plugin files in API project'); console.log('2. Handle new plugin files in API project'); console.log('3. Sync shared files between API and webgui'); + console.log('3b. Batch sync files (copy all API→WebGUI or WebGUI→API)'); console.log('4. Build web components'); console.log('5. Sync web components'); console.log('6. Exit\n'); - const answer = await UI.question('Choose an option (1-6): '); + const answer = await UI.question('Choose an option (1-6, 3b): '); switch (answer) { // ... existing cases + case '3b': { + UI.log('Batch sync mode activated...', 'info'); + const matchingFiles = Features.findMatchingFiles( + global.apiProjectDir, + global.webguiProjectDir + ); + + if (matchingFiles.size === 0) { + UI.log('No matching files found!', 'info'); + break; + } + + UI.log(`Found ${matchingFiles.size} matching files.`, 'info'); + const direction = await UI.question('Sync direction? (a=API→WebGUI, w=WebGUI→API, c=cancel): '); + + if (direction.toLowerCase() === 'c') { + UI.log('Batch sync canceled.', 'info'); + break; + } + + let syncCount = 0; + for (const [filename, paths] of matchingFiles) { + const [apiPath, webguiPath] = paths; + const apiHash = FileSystem.getFileHash(apiPath); + const webguiHash = FileSystem.getFileHash(webguiPath); + + if (apiHash !== webguiHash) { + if (direction.toLowerCase() === 'a' && FileSystem.copyFile(apiPath, webguiPath)) { + UI.log(`Copied to WebGUI: ${filename}`, 'success'); + syncCount++; + } else if (direction.toLowerCase() === 'w' && FileSystem.copyFile(webguiPath, apiPath)) { + UI.log(`Copied to API: ${filename}`, 'success'); + syncCount++; + } + } + } + + UI.log(`Batch sync complete. Synced ${syncCount} files.`, 'success'); + break; + }
625-629: LGTM! The error handling looks good here.The global error handling ensures that uncaught exceptions are properly logged before exiting.
| getFileHash(filePath) { | ||
| const fileBuffer = fs.readFileSync(filePath); | ||
| const hashSum = crypto.createHash('sha256'); | ||
| hashSum.update(fileBuffer); | ||
| return hashSum.digest('hex'); | ||
| }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Add error handling for file reads in getFileHash.
This function will throw an uncaught exception if the file doesn't exist or can't be read.
getFileHash(filePath) {
- const fileBuffer = fs.readFileSync(filePath);
- const hashSum = crypto.createHash('sha256');
- hashSum.update(fileBuffer);
- return hashSum.digest('hex');
+ try {
+ const fileBuffer = fs.readFileSync(filePath);
+ const hashSum = crypto.createHash('sha256');
+ hashSum.update(fileBuffer);
+ return hashSum.digest('hex');
+ } catch (err) {
+ UI.log(`Failed to get hash for ${filePath}: ${err.message}`, 'error');
+ return null;
+ }
},📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| getFileHash(filePath) { | |
| const fileBuffer = fs.readFileSync(filePath); | |
| const hashSum = crypto.createHash('sha256'); | |
| hashSum.update(fileBuffer); | |
| return hashSum.digest('hex'); | |
| }, | |
| getFileHash(filePath) { | |
| try { | |
| const fileBuffer = fs.readFileSync(filePath); | |
| const hashSum = crypto.createHash('sha256'); | |
| hashSum.update(fileBuffer); | |
| return hashSum.digest('hex'); | |
| } catch (err) { | |
| UI.log(`Failed to get hash for ${filePath}: ${err.message}`, 'error'); | |
| return null; | |
| } | |
| }, |
| playSound() { | ||
| const sounds = { | ||
| darwin: 'afplay /System/Library/Sounds/Glass.aiff', | ||
| linux: 'paplay /usr/share/sounds/freedesktop/stereo/complete.oga', | ||
| win32: | ||
| 'powershell.exe -c "(New-Object Media.SoundPlayer \'C:\\Windows\\Media\\Windows Default.wav\').PlaySync()"', | ||
| }; | ||
| const sound = sounds[process.platform]; | ||
| if (sound) require('child_process').exec(sound); | ||
| }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Consider error handling for playSound.
If the sound command fails, it could throw an uncaught exception. Also, there's a potential security risk with direct command execution.
playSound() {
const sounds = {
darwin: 'afplay /System/Library/Sounds/Glass.aiff',
linux: 'paplay /usr/share/sounds/freedesktop/stereo/complete.oga',
win32:
'powershell.exe -c "(New-Object Media.SoundPlayer \'C:\\Windows\\Media\\Windows Default.wav\').PlaySync()"',
};
const sound = sounds[process.platform];
- if (sound) require('child_process').exec(sound);
+ if (sound) {
+ try {
+ require('child_process').exec(sound, (error) => {
+ if (error) console.error('Sound playback error:', error);
+ });
+ } catch (err) {
+ console.error('Failed to play sound:', err);
+ }
+ }
},📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| playSound() { | |
| const sounds = { | |
| darwin: 'afplay /System/Library/Sounds/Glass.aiff', | |
| linux: 'paplay /usr/share/sounds/freedesktop/stereo/complete.oga', | |
| win32: | |
| 'powershell.exe -c "(New-Object Media.SoundPlayer \'C:\\Windows\\Media\\Windows Default.wav\').PlaySync()"', | |
| }; | |
| const sound = sounds[process.platform]; | |
| if (sound) require('child_process').exec(sound); | |
| }, | |
| playSound() { | |
| const sounds = { | |
| darwin: 'afplay /System/Library/Sounds/Glass.aiff', | |
| linux: 'paplay /usr/share/sounds/freedesktop/stereo/complete.oga', | |
| win32: | |
| 'powershell.exe -c "(New-Object Media.SoundPlayer \'C:\\Windows\\Media\\Windows Default.wav\').PlaySync()"', | |
| }; | |
| const sound = sounds[process.platform]; | |
| if (sound) { | |
| try { | |
| require('child_process').exec(sound, (error) => { | |
| if (error) console.error('Sound playback error:', error); | |
| }); | |
| } catch (err) { | |
| console.error('Failed to play sound:', err); | |
| } | |
| } | |
| }, |
|
|
||
| function walkDir(currentPath, baseDir) { | ||
| if (!fs.existsSync(currentPath)) { | ||
| UI.log(`Directory doesn't exist: ${currentPath}`, 'warning'); | ||
| return; | ||
| } | ||
|
|
||
| UI.log(`Checking directory: ${path.relative(apiProjectDir, currentPath)}`, 'info'); | ||
|
|
||
| fs.readdirSync(currentPath).forEach((file) => { | ||
| if (file.startsWith('.')) { | ||
| UI.log(`Skipping dot file/dir: ${file}`, 'skip'); | ||
| return; | ||
| } | ||
|
|
||
| const fullPath = path.join(currentPath, file); | ||
| const relativePath = path.relative(apiPluginsPath, fullPath); | ||
|
|
||
| if (CONSTANTS.IGNORE_PATTERNS.some((pattern) => pattern.test(file))) { | ||
| UI.log(`Skipping ignored pattern: ${file}`, 'skip'); | ||
| return; | ||
| } | ||
|
|
||
| if (gitignore1?.ignores(relativePath)) { | ||
| UI.log(`Skipping gitignored file: ${file}`, 'skip'); | ||
| return; | ||
| } | ||
|
|
||
| const lstat = fs.lstatSync(fullPath); | ||
| if (lstat.isSymbolicLink()) { | ||
| UI.log(`Skipping symlink: ${file}`, 'skip'); | ||
| return; | ||
| } | ||
|
|
||
| const stats = fs.statSync(fullPath); | ||
| if (stats.isDirectory()) { | ||
| UI.log(`Found subdirectory: ${file}`, 'info'); | ||
| walkDir(fullPath, baseDir); | ||
| return; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Consider extracting the walkDir function to a more reusable form.
The nested walkDir function duplicates logic from FileOps.walkDirectory. Consider refactoring to reduce code duplication.
- function walkDir(currentPath, baseDir) {
- if (!fs.existsSync(currentPath)) {
- UI.log(`Directory doesn't exist: ${currentPath}`, 'warning');
- return;
- }
-
- UI.log(`Checking directory: ${path.relative(apiProjectDir, currentPath)}`, 'info');
-
- fs.readdirSync(currentPath).forEach((file) => {
- if (file.startsWith('.')) {
- UI.log(`Skipping dot file/dir: ${file}`, 'skip');
- return;
- }
-
- const fullPath = path.join(currentPath, file);
- const relativePath = path.relative(apiPluginsPath, fullPath);
-
- if (CONSTANTS.IGNORE_PATTERNS.some((pattern) => pattern.test(file))) {
- UI.log(`Skipping ignored pattern: ${file}`, 'skip');
- return;
- }
-
- if (gitignore1?.ignores(relativePath)) {
- UI.log(`Skipping gitignored file: ${file}`, 'skip');
- return;
- }
-
- const lstat = fs.lstatSync(fullPath);
- if (lstat.isSymbolicLink()) {
- UI.log(`Skipping symlink: ${file}`, 'skip');
- return;
- }
-
- const stats = fs.statSync(fullPath);
- if (stats.isDirectory()) {
- UI.log(`Found subdirectory: ${file}`, 'info');
- walkDir(fullPath, baseDir);
- return;
- }
+ function walkDir(currentPath, baseDir) {
+ if (!fs.existsSync(currentPath)) {
+ UI.log(`Directory doesn't exist: ${currentPath}`, 'warning');
+ return;
+ }
+
+ UI.log(`Checking directory: ${path.relative(apiProjectDir, currentPath)}`, 'info');
+
+ try {
+ const files = fs.readdirSync(currentPath);
+
+ for (const file of files) {
+ if (file.startsWith('.')) {
+ UI.log(`Skipping dot file/dir: ${file}`, 'skip');
+ continue;
+ }
+
+ const fullPath = path.join(currentPath, file);
+ const relativePath = path.relative(apiPluginsPath, fullPath);
+
+ // Skip files matching ignore patterns
+ if (CONSTANTS.IGNORE_PATTERNS.some((pattern) => pattern.test(file))) {
+ UI.log(`Skipping ignored pattern: ${file}`, 'skip');
+ continue;
+ }
+
+ if (gitignore1?.ignores(relativePath)) {
+ UI.log(`Skipping gitignored file: ${file}`, 'skip');
+ continue;
+ }
+
+ try {
+ const lstat = fs.lstatSync(fullPath);
+ if (lstat.isSymbolicLink()) {
+ UI.log(`Skipping symlink: ${file}`, 'skip');
+ continue;
+ }
+
+ const stats = fs.statSync(fullPath);
+ if (stats.isDirectory()) {
+ UI.log(`Found subdirectory: ${file}`, 'info');
+ walkDir(fullPath, baseDir);
+ continue;
+ }Committable suggestion skipped: line range outside the PR's diff.
🤖 I have created a release *beep* *boop* --- ## [4.5.0](v4.4.1...v4.5.0) (2025-04-02) ### Features * add webgui theme switcher component ([#1304](#1304)) ([e2d00dc](e2d00dc)) * api plugin system & offline versioned dependency vendoring ([#1252](#1252)) ([9f492bf](9f492bf)) * **api:** add `unraid-api --delete` command ([#1289](#1289)) ([2f09445](2f09445)) * basic array controls ([#1291](#1291)) ([61fe696](61fe696)) * basic docker controls ([#1292](#1292)) ([12eddf8](12eddf8)) * copy to webgui repo script docs + wc build options ([#1285](#1285)) ([e54f189](e54f189)) ### Bug Fixes * additional url fixes ([4b2763c](4b2763c)) * **api:** redirect benign pnpm postinstall warning to log file ([#1290](#1290)) ([7fb7849](7fb7849)) * **deps:** update dependency chalk to v5 ([#1296](#1296)) ([6bed638](6bed638)) * **deps:** update dependency diff to v7 ([#1297](#1297)) ([3c6683c](3c6683c)) * disable all config watchers ([#1306](#1306)) ([5c1b435](5c1b435)) * extract callbacks to library ([#1280](#1280)) ([2266139](2266139)) * OEM plugin issues ([#1288](#1288)) ([d5a3d0d](d5a3d0d)) * replace files lost during pruning ([d0d2ff6](d0d2ff6)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Previously the historical script only use to copy built web components into the webgui repo.
The new script provides additional commands to find files in the API repo's plugin dir and attempts to find "new" files. Then a command to act upon each of these new file to sync in either direction, skip it, or ignore it.
Summary by CodeRabbit
New Features
Chores