Fix CI/CD problems
This commit is contained in:
@@ -2,6 +2,44 @@ import { describe, it, expect, beforeEach, vi } from 'vitest'
|
||||
import { render, screen, fireEvent } from '@testing-library/react'
|
||||
import Home from '../page'
|
||||
|
||||
// Mock tRPC
|
||||
vi.mock('~/trpc/react', () => ({
|
||||
api: {
|
||||
scripts: {
|
||||
getRepoStatus: {
|
||||
useQuery: vi.fn(() => ({
|
||||
data: { isRepo: true, isBehind: false, branch: 'main', lastCommit: 'abc123' },
|
||||
refetch: vi.fn(),
|
||||
})),
|
||||
},
|
||||
getScriptCards: {
|
||||
useQuery: vi.fn(() => ({
|
||||
data: { success: true, cards: [] },
|
||||
isLoading: false,
|
||||
error: null,
|
||||
})),
|
||||
},
|
||||
getCtScripts: {
|
||||
useQuery: vi.fn(() => ({
|
||||
data: { scripts: [] },
|
||||
isLoading: false,
|
||||
error: null,
|
||||
})),
|
||||
},
|
||||
getScriptBySlug: {
|
||||
useQuery: vi.fn(() => ({
|
||||
data: null,
|
||||
})),
|
||||
},
|
||||
fullUpdateRepo: {
|
||||
useMutation: vi.fn(() => ({
|
||||
mutate: vi.fn(),
|
||||
})),
|
||||
},
|
||||
},
|
||||
},
|
||||
}))
|
||||
|
||||
// Mock child components
|
||||
vi.mock('../_components/ScriptsGrid', () => ({
|
||||
ScriptsGrid: ({ onInstallScript }: { onInstallScript?: (path: string, name: string) => void }) => (
|
||||
|
||||
@@ -9,14 +9,17 @@ const execAsync = promisify(exec);
|
||||
export class GitManager {
|
||||
private git: SimpleGit;
|
||||
private repoPath: string;
|
||||
private scriptsDir: string;
|
||||
private scriptsDir: string | null = null;
|
||||
|
||||
constructor() {
|
||||
this.repoPath = process.cwd();
|
||||
this.scriptsDir = join(this.repoPath, env.SCRIPTS_DIRECTORY);
|
||||
this.git = simpleGit(this.repoPath);
|
||||
}
|
||||
|
||||
private initializeConfig() {
|
||||
this.scriptsDir ??= join(this.repoPath, env.SCRIPTS_DIRECTORY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the repository is behind the remote
|
||||
*/
|
||||
|
||||
@@ -16,38 +16,45 @@ export interface ScriptInfo {
|
||||
}
|
||||
|
||||
export class ScriptManager {
|
||||
private scriptsDir: string;
|
||||
private allowedExtensions: string[];
|
||||
private allowedPaths: string[];
|
||||
private maxExecutionTime: number;
|
||||
private scriptsDir: string | null = null;
|
||||
private allowedExtensions: string[] | null = null;
|
||||
private allowedPaths: string[] | null = null;
|
||||
private maxExecutionTime: number | null = null;
|
||||
|
||||
constructor() {
|
||||
// Handle both absolute and relative paths for testing
|
||||
this.scriptsDir = env.SCRIPTS_DIRECTORY.startsWith('/')
|
||||
? env.SCRIPTS_DIRECTORY
|
||||
: join(process.cwd(), env.SCRIPTS_DIRECTORY);
|
||||
this.allowedExtensions = env.ALLOWED_SCRIPT_EXTENSIONS.split(',').map(ext => ext.trim());
|
||||
this.allowedPaths = env.ALLOWED_SCRIPT_PATHS.split(',').map(path => path.trim());
|
||||
this.maxExecutionTime = parseInt(env.MAX_SCRIPT_EXECUTION_TIME, 10);
|
||||
// Initialize lazily to avoid accessing env vars during module load
|
||||
}
|
||||
|
||||
private initializeConfig() {
|
||||
if (this.scriptsDir === null) {
|
||||
// Handle both absolute and relative paths for testing
|
||||
this.scriptsDir = env.SCRIPTS_DIRECTORY.startsWith('/')
|
||||
? env.SCRIPTS_DIRECTORY
|
||||
: join(process.cwd(), env.SCRIPTS_DIRECTORY);
|
||||
this.allowedExtensions = env.ALLOWED_SCRIPT_EXTENSIONS.split(',').map(ext => ext.trim());
|
||||
this.allowedPaths = env.ALLOWED_SCRIPT_PATHS.split(',').map(path => path.trim());
|
||||
this.maxExecutionTime = parseInt(env.MAX_SCRIPT_EXECUTION_TIME, 10);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all available scripts in the scripts directory
|
||||
*/
|
||||
async getScripts(): Promise<ScriptInfo[]> {
|
||||
this.initializeConfig();
|
||||
try {
|
||||
const files = await readdir(this.scriptsDir);
|
||||
const files = await readdir(this.scriptsDir!);
|
||||
const scripts: ScriptInfo[] = [];
|
||||
|
||||
for (const file of files) {
|
||||
const filePath = join(this.scriptsDir, file);
|
||||
const filePath = join(this.scriptsDir!, file);
|
||||
const stats = await stat(filePath);
|
||||
|
||||
if (stats.isFile()) {
|
||||
const extension = extname(file);
|
||||
|
||||
// Check if file extension is allowed
|
||||
if (this.allowedExtensions.includes(extension)) {
|
||||
if (this.allowedExtensions!.includes(extension)) {
|
||||
// Check if file is executable
|
||||
const executable = await this.isExecutable(filePath);
|
||||
|
||||
@@ -74,8 +81,9 @@ export class ScriptManager {
|
||||
* Get all available scripts in the ct subdirectory
|
||||
*/
|
||||
async getCtScripts(): Promise<ScriptInfo[]> {
|
||||
this.initializeConfig();
|
||||
try {
|
||||
const ctDir = join(this.scriptsDir, 'ct');
|
||||
const ctDir = join(this.scriptsDir!, 'ct');
|
||||
const files = await readdir(ctDir);
|
||||
const scripts: ScriptInfo[] = [];
|
||||
|
||||
@@ -87,7 +95,7 @@ export class ScriptManager {
|
||||
const extension = extname(file);
|
||||
|
||||
// Check if file extension is allowed
|
||||
if (this.allowedExtensions.includes(extension)) {
|
||||
if (this.allowedExtensions!.includes(extension)) {
|
||||
// Check if file is executable
|
||||
const executable = await this.isExecutable(filePath);
|
||||
|
||||
@@ -143,8 +151,9 @@ export class ScriptManager {
|
||||
* Validate if a script path is allowed to be executed
|
||||
*/
|
||||
validateScriptPath(scriptPath: string): { valid: boolean; message?: string } {
|
||||
this.initializeConfig();
|
||||
const resolvedPath = resolve(scriptPath);
|
||||
const scriptsDirResolved = resolve(this.scriptsDir);
|
||||
const scriptsDirResolved = resolve(this.scriptsDir!);
|
||||
|
||||
// Check if the script is within the allowed directory
|
||||
if (!resolvedPath.startsWith(scriptsDirResolved)) {
|
||||
@@ -158,7 +167,7 @@ export class ScriptManager {
|
||||
const relativePath = resolvedPath.replace(scriptsDirResolved, '').replace(/\\/g, '/');
|
||||
const normalizedRelativePath = relativePath.startsWith('/') ? relativePath : '/' + relativePath;
|
||||
|
||||
const isAllowed = this.allowedPaths.some(allowedPath => {
|
||||
const isAllowed = this.allowedPaths!.some(allowedPath => {
|
||||
const normalizedAllowedPath = allowedPath.startsWith('/') ? allowedPath : '/' + allowedPath;
|
||||
// For root path '/', allow files directly in the scripts directory (no subdirectories)
|
||||
if (normalizedAllowedPath === '/') {
|
||||
@@ -177,10 +186,10 @@ export class ScriptManager {
|
||||
|
||||
// Check file extension
|
||||
const extension = extname(scriptPath);
|
||||
if (!this.allowedExtensions.includes(extension)) {
|
||||
if (!this.allowedExtensions!.includes(extension)) {
|
||||
return {
|
||||
valid: false,
|
||||
message: `File extension '${extension}' is not allowed. Allowed extensions: ${this.allowedExtensions.join(', ')}`
|
||||
message: `File extension '${extension}' is not allowed. Allowed extensions: ${this.allowedExtensions!.join(', ')}`
|
||||
};
|
||||
}
|
||||
|
||||
@@ -227,7 +236,7 @@ export class ScriptManager {
|
||||
|
||||
// Spawn the process
|
||||
const childProcess = spawn(command, args, {
|
||||
cwd: this.scriptsDir,
|
||||
cwd: this.scriptsDir!,
|
||||
stdio: ['pipe', 'pipe', 'pipe'],
|
||||
shell: true
|
||||
});
|
||||
@@ -237,7 +246,7 @@ export class ScriptManager {
|
||||
if (!childProcess.killed) {
|
||||
childProcess.kill('SIGTERM');
|
||||
}
|
||||
}, this.maxExecutionTime);
|
||||
}, this.maxExecutionTime!);
|
||||
|
||||
// Clean up timeout when process exits
|
||||
childProcess.on('exit', () => {
|
||||
@@ -268,11 +277,12 @@ export class ScriptManager {
|
||||
allowedPaths: string[];
|
||||
maxExecutionTime: number;
|
||||
} {
|
||||
this.initializeConfig();
|
||||
return {
|
||||
path: this.scriptsDir,
|
||||
allowedExtensions: this.allowedExtensions,
|
||||
allowedPaths: this.allowedPaths,
|
||||
maxExecutionTime: this.maxExecutionTime
|
||||
path: this.scriptsDir!,
|
||||
allowedExtensions: this.allowedExtensions!,
|
||||
allowedPaths: this.allowedPaths!,
|
||||
maxExecutionTime: this.maxExecutionTime!
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,37 +4,44 @@ import { env } from '~/env.js';
|
||||
import type { Script, ScriptCard, GitHubFile } from '~/types/script';
|
||||
|
||||
export class GitHubJsonService {
|
||||
private baseUrl: string;
|
||||
private repoUrl: string;
|
||||
private branch: string;
|
||||
private jsonFolder: string;
|
||||
private localJsonDirectory: string;
|
||||
private baseUrl: string | null = null;
|
||||
private repoUrl: string | null = null;
|
||||
private branch: string | null = null;
|
||||
private jsonFolder: string | null = null;
|
||||
private localJsonDirectory: string | null = null;
|
||||
private scriptCache: Map<string, Script> = new Map();
|
||||
|
||||
constructor() {
|
||||
this.repoUrl = env.REPO_URL ?? "";
|
||||
this.branch = env.REPO_BRANCH;
|
||||
this.jsonFolder = env.JSON_FOLDER;
|
||||
this.localJsonDirectory = join(process.cwd(), 'scripts', 'json');
|
||||
|
||||
// Only validate GitHub URL if it's provided
|
||||
if (this.repoUrl) {
|
||||
// Extract owner and repo from the URL
|
||||
const urlMatch = /github\.com\/([^\/]+)\/([^\/]+)/.exec(this.repoUrl);
|
||||
if (!urlMatch) {
|
||||
throw new Error(`Invalid GitHub repository URL: ${this.repoUrl}`);
|
||||
}
|
||||
// Initialize lazily to avoid accessing env vars during module load
|
||||
}
|
||||
|
||||
private initializeConfig() {
|
||||
if (this.repoUrl === null) {
|
||||
this.repoUrl = env.REPO_URL ?? "";
|
||||
this.branch = env.REPO_BRANCH;
|
||||
this.jsonFolder = env.JSON_FOLDER;
|
||||
this.localJsonDirectory = join(process.cwd(), 'scripts', 'json');
|
||||
|
||||
const [, owner, repo] = urlMatch;
|
||||
this.baseUrl = `https://api.github.com/repos/${owner}/${repo}`;
|
||||
} else {
|
||||
// Set a dummy base URL if no REPO_URL is provided
|
||||
this.baseUrl = "";
|
||||
// Only validate GitHub URL if it's provided
|
||||
if (this.repoUrl) {
|
||||
// Extract owner and repo from the URL
|
||||
const urlMatch = /github\.com\/([^\/]+)\/([^\/]+)/.exec(this.repoUrl);
|
||||
if (!urlMatch) {
|
||||
throw new Error(`Invalid GitHub repository URL: ${this.repoUrl}`);
|
||||
}
|
||||
|
||||
const [, owner, repo] = urlMatch;
|
||||
this.baseUrl = `https://api.github.com/repos/${owner}/${repo}`;
|
||||
} else {
|
||||
// Set a dummy base URL if no REPO_URL is provided
|
||||
this.baseUrl = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async fetchFromGitHub<T>(endpoint: string): Promise<T> {
|
||||
const response = await fetch(`${this.baseUrl}${endpoint}`, {
|
||||
this.initializeConfig();
|
||||
const response = await fetch(`${this.baseUrl!}${endpoint}`, {
|
||||
headers: {
|
||||
'Accept': 'application/vnd.github.v3+json',
|
||||
'User-Agent': 'PVEScripts-Local/1.0',
|
||||
@@ -49,7 +56,8 @@ export class GitHubJsonService {
|
||||
}
|
||||
|
||||
private async downloadJsonFile(filePath: string): Promise<Script> {
|
||||
const rawUrl = `https://raw.githubusercontent.com/${this.extractRepoPath()}/${this.branch}/${filePath}`;
|
||||
this.initializeConfig();
|
||||
const rawUrl = `https://raw.githubusercontent.com/${this.extractRepoPath()}/${this.branch!}/${filePath}`;
|
||||
|
||||
const response = await fetch(rawUrl);
|
||||
if (!response.ok) {
|
||||
@@ -61,7 +69,8 @@ export class GitHubJsonService {
|
||||
}
|
||||
|
||||
private extractRepoPath(): string {
|
||||
const match = /github\.com\/([^\/]+)\/([^\/]+)/.exec(this.repoUrl);
|
||||
this.initializeConfig();
|
||||
const match = /github\.com\/([^\/]+)\/([^\/]+)/.exec(this.repoUrl!);
|
||||
if (!match) {
|
||||
throw new Error('Invalid GitHub repository URL');
|
||||
}
|
||||
@@ -69,13 +78,14 @@ export class GitHubJsonService {
|
||||
}
|
||||
|
||||
async getJsonFiles(): Promise<GitHubFile[]> {
|
||||
this.initializeConfig();
|
||||
if (!this.repoUrl) {
|
||||
throw new Error('REPO_URL environment variable is not set. Cannot fetch from GitHub.');
|
||||
}
|
||||
|
||||
try {
|
||||
const files = await this.fetchFromGitHub<GitHubFile[]>(
|
||||
`/contents/${this.jsonFolder}?ref=${this.branch}`
|
||||
`/contents/${this.jsonFolder!}?ref=${this.branch!}`
|
||||
);
|
||||
|
||||
// Filter for JSON files only
|
||||
@@ -139,7 +149,8 @@ export class GitHubJsonService {
|
||||
|
||||
// If not found locally, try to download just this specific script
|
||||
try {
|
||||
const script = await this.downloadJsonFile(`${this.jsonFolder}/${slug}.json`);
|
||||
this.initializeConfig();
|
||||
const script = await this.downloadJsonFile(`${this.jsonFolder!}/${slug}.json`);
|
||||
return script;
|
||||
} catch {
|
||||
console.log(`Script ${slug} not found in repository`);
|
||||
@@ -161,7 +172,8 @@ export class GitHubJsonService {
|
||||
const { readFile } = await import('fs/promises');
|
||||
const { join } = await import('path');
|
||||
|
||||
const filePath = join(this.localJsonDirectory, `${slug}.json`);
|
||||
this.initializeConfig();
|
||||
const filePath = join(this.localJsonDirectory!, `${slug}.json`);
|
||||
const content = await readFile(filePath, 'utf-8');
|
||||
const script = JSON.parse(content) as Script;
|
||||
|
||||
@@ -198,14 +210,15 @@ export class GitHubJsonService {
|
||||
}
|
||||
|
||||
private async saveScriptsLocally(scripts: Script[]): Promise<void> {
|
||||
this.initializeConfig();
|
||||
try {
|
||||
// Ensure the directory exists
|
||||
await mkdir(this.localJsonDirectory, { recursive: true });
|
||||
await mkdir(this.localJsonDirectory!, { recursive: true });
|
||||
|
||||
// Save each script as a JSON file
|
||||
for (const script of scripts) {
|
||||
const filename = `${script.slug}.json`;
|
||||
const filePath = join(this.localJsonDirectory, filename);
|
||||
const filePath = join(this.localJsonDirectory!, filename);
|
||||
const content = JSON.stringify(script, null, 2);
|
||||
await writeFile(filePath, content, 'utf-8');
|
||||
}
|
||||
|
||||
@@ -4,12 +4,18 @@ import { env } from '~/env.js';
|
||||
import type { Script } from '~/types/script';
|
||||
|
||||
export class ScriptDownloaderService {
|
||||
private scriptsDirectory: string;
|
||||
private repoUrl: string;
|
||||
private scriptsDirectory: string | null = null;
|
||||
private repoUrl: string | null = null;
|
||||
|
||||
constructor() {
|
||||
this.scriptsDirectory = join(process.cwd(), 'scripts');
|
||||
this.repoUrl = env.REPO_URL ?? '';
|
||||
// Initialize lazily to avoid accessing env vars during module load
|
||||
}
|
||||
|
||||
private initializeConfig() {
|
||||
if (this.scriptsDirectory === null) {
|
||||
this.scriptsDirectory = join(process.cwd(), 'scripts');
|
||||
this.repoUrl = env.REPO_URL ?? '';
|
||||
}
|
||||
}
|
||||
|
||||
private async ensureDirectoryExists(dirPath: string): Promise<void> {
|
||||
@@ -21,6 +27,7 @@ export class ScriptDownloaderService {
|
||||
}
|
||||
|
||||
private async downloadFileFromGitHub(filePath: string): Promise<string> {
|
||||
this.initializeConfig();
|
||||
if (!this.repoUrl) {
|
||||
throw new Error('REPO_URL environment variable is not set');
|
||||
}
|
||||
@@ -36,7 +43,8 @@ export class ScriptDownloaderService {
|
||||
}
|
||||
|
||||
private extractRepoPath(): string {
|
||||
const match = /github\.com\/([^\/]+)\/([^\/]+)/.exec(this.repoUrl);
|
||||
this.initializeConfig();
|
||||
const match = /github\.com\/([^\/]+)\/([^\/]+)/.exec(this.repoUrl!);
|
||||
if (!match) {
|
||||
throw new Error('Invalid GitHub repository URL');
|
||||
}
|
||||
@@ -53,14 +61,15 @@ export class ScriptDownloaderService {
|
||||
}
|
||||
|
||||
async loadScript(script: Script): Promise<{ success: boolean; message: string; files: string[] }> {
|
||||
this.initializeConfig();
|
||||
try {
|
||||
const files: string[] = [];
|
||||
|
||||
// Ensure directories exist
|
||||
await this.ensureDirectoryExists(join(this.scriptsDirectory, 'ct'));
|
||||
await this.ensureDirectoryExists(join(this.scriptsDirectory, 'install'));
|
||||
await this.ensureDirectoryExists(join(this.scriptsDirectory, 'tools'));
|
||||
await this.ensureDirectoryExists(join(this.scriptsDirectory, 'vm'));
|
||||
await this.ensureDirectoryExists(join(this.scriptsDirectory!, 'ct'));
|
||||
await this.ensureDirectoryExists(join(this.scriptsDirectory!, 'install'));
|
||||
await this.ensureDirectoryExists(join(this.scriptsDirectory!, 'tools'));
|
||||
await this.ensureDirectoryExists(join(this.scriptsDirectory!, 'vm'));
|
||||
|
||||
if (script.install_methods?.length) {
|
||||
for (const method of script.install_methods) {
|
||||
@@ -80,28 +89,28 @@ export class ScriptDownloaderService {
|
||||
targetDir = 'ct';
|
||||
// Modify the content for CT scripts
|
||||
const modifiedContent = this.modifyScriptContent(content);
|
||||
filePath = join(this.scriptsDirectory, targetDir, fileName);
|
||||
filePath = join(this.scriptsDirectory!, targetDir, fileName);
|
||||
await writeFile(filePath, modifiedContent, 'utf-8');
|
||||
} else if (scriptPath.startsWith('tools/')) {
|
||||
targetDir = 'tools';
|
||||
// Don't modify content for tools scripts
|
||||
filePath = join(this.scriptsDirectory, targetDir, fileName);
|
||||
filePath = join(this.scriptsDirectory!, targetDir, fileName);
|
||||
await writeFile(filePath, content, 'utf-8');
|
||||
} else if (scriptPath.startsWith('vm/')) {
|
||||
targetDir = 'vm';
|
||||
// Don't modify content for VM scripts
|
||||
filePath = join(this.scriptsDirectory, targetDir, fileName);
|
||||
filePath = join(this.scriptsDirectory!, targetDir, fileName);
|
||||
await writeFile(filePath, content, 'utf-8');
|
||||
} else if (scriptPath.startsWith('vw/')) {
|
||||
targetDir = 'vw';
|
||||
// Don't modify content for VW scripts
|
||||
filePath = join(this.scriptsDirectory, targetDir, fileName);
|
||||
filePath = join(this.scriptsDirectory!, targetDir, fileName);
|
||||
await writeFile(filePath, content, 'utf-8');
|
||||
} else {
|
||||
// Handle other script types (fallback to ct directory)
|
||||
targetDir = 'ct';
|
||||
const modifiedContent = this.modifyScriptContent(content);
|
||||
filePath = join(this.scriptsDirectory, targetDir, fileName);
|
||||
filePath = join(this.scriptsDirectory!, targetDir, fileName);
|
||||
await writeFile(filePath, modifiedContent, 'utf-8');
|
||||
}
|
||||
|
||||
@@ -117,7 +126,7 @@ export class ScriptDownloaderService {
|
||||
const installScriptName = `${script.slug}-install.sh`;
|
||||
try {
|
||||
const installContent = await this.downloadFileFromGitHub(`install/${installScriptName}`);
|
||||
const localInstallPath = join(this.scriptsDirectory, 'install', installScriptName);
|
||||
const localInstallPath = join(this.scriptsDirectory!, 'install', installScriptName);
|
||||
await writeFile(localInstallPath, installContent, 'utf-8');
|
||||
files.push(`install/${installScriptName}`);
|
||||
} catch {
|
||||
@@ -141,6 +150,7 @@ export class ScriptDownloaderService {
|
||||
}
|
||||
|
||||
async checkScriptExists(script: Script): Promise<{ ctExists: boolean; installExists: boolean; files: string[] }> {
|
||||
this.initializeConfig();
|
||||
const files: string[] = [];
|
||||
let ctExists = false;
|
||||
let installExists = false;
|
||||
@@ -159,7 +169,7 @@ export class ScriptDownloaderService {
|
||||
|
||||
if (scriptPath.startsWith('ct/')) {
|
||||
targetDir = 'ct';
|
||||
localPath = join(this.scriptsDirectory, targetDir, fileName);
|
||||
localPath = join(this.scriptsDirectory!, targetDir, fileName);
|
||||
try {
|
||||
await readFile(localPath, 'utf-8');
|
||||
ctExists = true;
|
||||
@@ -169,7 +179,7 @@ export class ScriptDownloaderService {
|
||||
}
|
||||
} else if (scriptPath.startsWith('tools/')) {
|
||||
targetDir = 'tools';
|
||||
localPath = join(this.scriptsDirectory, targetDir, fileName);
|
||||
localPath = join(this.scriptsDirectory!, targetDir, fileName);
|
||||
try {
|
||||
await readFile(localPath, 'utf-8');
|
||||
ctExists = true; // Use ctExists for tools scripts too for UI consistency
|
||||
@@ -179,7 +189,7 @@ export class ScriptDownloaderService {
|
||||
}
|
||||
} else if (scriptPath.startsWith('vm/')) {
|
||||
targetDir = 'vm';
|
||||
localPath = join(this.scriptsDirectory, targetDir, fileName);
|
||||
localPath = join(this.scriptsDirectory!, targetDir, fileName);
|
||||
try {
|
||||
await readFile(localPath, 'utf-8');
|
||||
ctExists = true; // Use ctExists for VM scripts too for UI consistency
|
||||
@@ -189,7 +199,7 @@ export class ScriptDownloaderService {
|
||||
}
|
||||
} else if (scriptPath.startsWith('vw/')) {
|
||||
targetDir = 'vw';
|
||||
localPath = join(this.scriptsDirectory, targetDir, fileName);
|
||||
localPath = join(this.scriptsDirectory!, targetDir, fileName);
|
||||
try {
|
||||
await readFile(localPath, 'utf-8');
|
||||
ctExists = true; // Use ctExists for VW scripts too for UI consistency
|
||||
@@ -207,7 +217,7 @@ export class ScriptDownloaderService {
|
||||
const hasCtScript = script.install_methods?.some(method => method.script?.startsWith('ct/'));
|
||||
if (hasCtScript) {
|
||||
const installScriptName = `${script.slug}-install.sh`;
|
||||
const localInstallPath = join(this.scriptsDirectory, 'install', installScriptName);
|
||||
const localInstallPath = join(this.scriptsDirectory!, 'install', installScriptName);
|
||||
try {
|
||||
await readFile(localInstallPath, 'utf-8');
|
||||
installExists = true;
|
||||
@@ -225,6 +235,7 @@ export class ScriptDownloaderService {
|
||||
}
|
||||
|
||||
async compareScriptContent(script: Script): Promise<{ hasDifferences: boolean; differences: string[] }> {
|
||||
this.initializeConfig();
|
||||
const differences: string[] = [];
|
||||
let hasDifferences = false;
|
||||
|
||||
@@ -310,7 +321,7 @@ export class ScriptDownloaderService {
|
||||
|
||||
private async compareSingleFile(remotePath: string, filePath: string): Promise<{ hasDifferences: boolean; filePath: string }> {
|
||||
try {
|
||||
const localPath = join(this.scriptsDirectory, filePath);
|
||||
const localPath = join(this.scriptsDirectory!, filePath);
|
||||
|
||||
// Read local content
|
||||
const localContent = await readFile(localPath, 'utf-8');
|
||||
@@ -337,6 +348,7 @@ export class ScriptDownloaderService {
|
||||
}
|
||||
|
||||
async getScriptDiff(script: Script, filePath: string): Promise<{ diff: string | null; localContent: string | null; remoteContent: string | null }> {
|
||||
this.initializeConfig();
|
||||
try {
|
||||
let localContent: string | null = null;
|
||||
let remoteContent: string | null = null;
|
||||
@@ -345,7 +357,7 @@ export class ScriptDownloaderService {
|
||||
// Handle CT script
|
||||
const fileName = filePath.split('/').pop();
|
||||
if (fileName) {
|
||||
const localPath = join(this.scriptsDirectory, 'ct', fileName);
|
||||
const localPath = join(this.scriptsDirectory!, 'ct', fileName);
|
||||
try {
|
||||
localContent = await readFile(localPath, 'utf-8');
|
||||
} catch {
|
||||
@@ -365,7 +377,7 @@ export class ScriptDownloaderService {
|
||||
}
|
||||
} else if (filePath.startsWith('install/')) {
|
||||
// Handle install script
|
||||
const localPath = join(this.scriptsDirectory, filePath);
|
||||
const localPath = join(this.scriptsDirectory!, filePath);
|
||||
try {
|
||||
localContent = await readFile(localPath, 'utf-8');
|
||||
} catch {
|
||||
|
||||
Reference in New Issue
Block a user