Skip to content
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

Refactored core modules and improved application architecture #2

Merged
merged 2 commits into from
Jan 12, 2025
Merged

Conversation

phukon
Copy link
Owner

@phukon phukon commented Jan 12, 2025

Summary by CodeRabbit

Release Notes

  • New Features

    • Added automated release workflow using GitHub Actions
    • Introduced system dependency checks for Git and GPG
    • Enhanced key generation and configuration processes
  • Improvements

    • Refactored configuration and utility functions
    • Improved error handling and logging
    • Streamlined Git and GPG setup procedures
  • Dependency Updates

    • Added semantic-release for managing release processes
    • Updated project scripts for better release management
  • Code Organization

    • Reorganized utility and command modules
    • Simplified import paths and module structures

…nd enhance error handling

- Refactored core modules for better maintainability and scalability
- Introduced comprehensive type annotations to improve type safety
- Enhanced error handling with more descriptive messages and structured exception management
- Updated existing components to align with the new architecture
- Improved overall application performance and reliability
Copy link

coderabbitai bot commented Jan 12, 2025

Walkthrough

A comprehensive refactoring of the project's configuration and initialization processes has been implemented. The changes focus on improving dependency checks, key generation, and system configuration for Git and GPG. A new GitHub Actions workflow for release management has been added, along with semantic release configuration. The project now includes more robust error handling, asynchronous operations, and improved system compatibility, particularly for checking and setting up GPG and Git configurations across different platforms.

Changes

File Change Summary
.github/workflows/release.yml New GitHub Actions workflow for automated releases
bin/index.ts Updated import paths for importKey and createLogger
package.json Added semantic-release script and development dependency
release.config.cjs New release configuration for main and next branches
src/systemCheck.ts New module for checking GPG and Git installations
src/commands/start.ts Refactored start function with improved dependency and configuration checks
src/utils/checkDependencies.ts Added checkRequiredDependencies function
src/utils/checkSecretKeys.ts Refactored to asynchronous key checking
src/utils/createKey.ts New createPgpKey function for interactive key generation
src/utils/linuxConfig.ts New utility for Linux-specific GPG configuration
src/utils/setGitConfig.ts Enhanced Git configuration with improved key fingerprint extraction

Sequence Diagram

sequenceDiagram
    participant User
    participant CLI
    participant SystemCheck
    participant GitConfig
    participant GPGConfig
    participant KeyGen

    User->>CLI: Start command
    CLI->>SystemCheck: Check dependencies
    SystemCheck-->>CLI: Dependency status
    alt Dependencies OK
        CLI->>GPGConfig: Check existing keys
        GPGConfig-->>CLI: Key status
        alt No existing keys
            CLI->>KeyGen: Create PGP key
            KeyGen-->>CLI: Key generation result
        end
        CLI->>GitConfig: Configure Git
        GitConfig-->>CLI: Configuration status
        CLI->>User: Display result
    else Dependencies Missing
        CLI->>User: Show error
    end
Loading

Poem

🐰 A rabbit's release, oh so neat!
Workflows dancing to a semantic beat
Dependencies checked with care
Git and GPG, a configuration flair
Releasing code with magical might! 🚀

Finishing Touches

  • 📝 Generate Docstrings (Beta)

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

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)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@phukon phukon changed the title Refactored core modules and improved architecture Refactored core modules and improved application architecture Jan 12, 2025
@phukon phukon merged commit 14658c5 into main Jan 12, 2025
1 of 2 checks passed
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 7

🧹 Nitpick comments (9)
src/utils/checkSecretKeys.ts (1)

20-22: Review error handling for the GPG process

The error event on the gpgProcess is emitted only if the process couldn't be spawned or there was an issue during spawning. It does not capture errors from the GPG command itself. Consider handling errors from stderr to catch issues reported by GPG.

You can listen to gpgProcess.stderr to capture error messages from GPG:

     gpgProcess.on('error', () => {
       resolve(GitKeyKitCodes.ERR_NO_SECRET_KEYS);
     });

+    gpgProcess.stderr.on('data', (data: Buffer) => {
+      const errorOutput = data.toString();
+      // Handle specific GPG error messages if necessary
+    });
src/commands/start.ts (2)

7-7: Remove commented-out import statement

The import of chalk is commented out. If it's not needed, consider removing it to keep the code clean.

Apply this diff to remove the unused import:

-// import chalk from 'chalk';

54-56: Include stack trace in error logging for better debugging

When logging unexpected errors, including the stack trace can aid in diagnosing issues more effectively.

Apply this diff to enhance error logging:

     if (error instanceof Error) {
-      logger.error(`Unexpected error: ${error.message}`);
+      logger.error(`Unexpected error: ${error.stack || error.message}`);
     }
src/utils/linuxConfig.ts (2)

12-20: Simplify createDirectory function by removing unnecessary error handling

Using mkdir with { recursive: true } will not throw an error if the directory already exists. The catch block checking for 'EEXIST' can be removed to simplify the code.

Apply this diff to simplify the function:

 async function createDirectory(path: string): Promise<void> {
-  try {
     await mkdir(path, { mode: 0o700, recursive: true });
-  } catch (error) {
-    if ((error as { code?: string }).code !== "EEXIST") {
-      throw error;
-    }
-  }
 }

84-84: Improve error logging in the catch block

Directly logging error may not provide meaningful information. Casting error to Error and logging the message can offer clearer insights.

Apply this diff to enhance error logging:

     console.error(chalk.red(`Unexpected error: ${error}`));
+    console.error(chalk.red(`Unexpected error: ${(error as Error).message}`));
src/utils/checkDependencies.ts (1)

4-7: Update function documentation to accurately reflect the return type

The JSDoc comment indicates that the function returns a promise resolving to GPG path or error code. However, the function actually returns an object containing the status code and optionally the GPG path.

Consider updating the comment to match the actual return value:

 /**
- * Checks all required dependencies
- * @returns Promise resolving to GPG path or error code
+ * Checks all required dependencies.
+ * @returns {Promise<{ code: GitKeyKitCodes, gpgPath?: string }>} An object containing the status code and optional GPG path.
  */
src/systemCheck.ts (2)

8-11: Consider handling unsupported platforms explicitly

While the commands are correctly defined for Windows and Unix-like systems, consider adding explicit handling for unsupported platforms to prevent runtime errors.

 const COMMANDS = {
-  GPG_CHECK: platform() === 'win32' ? 'where gpg' : 'which gpg',
-  GIT_CHECK: platform() === 'win32' ? 'where git' : 'which git'
+  GPG_CHECK: (() => {
+    switch (platform()) {
+      case 'win32': return 'where gpg';
+      case 'linux':
+      case 'darwin': return 'which gpg';
+      default: throw new Error(`Unsupported platform: ${platform()}`);
+    }
+  })(),
+  GIT_CHECK: (() => {
+    switch (platform()) {
+      case 'win32': return 'where git';
+      case 'linux':
+      case 'darwin': return 'which git';
+      default: throw new Error(`Unsupported platform: ${platform()}`);
+    }
+  })()
 };

43-50: Maintain consistency with GPG check implementation

For consistency and robustness, consider applying similar improvements as suggested for the GPG check:

  1. More specific error handling
  2. Validation of Git executable
 export async function checkGitInstallation(): Promise<GitKeyKitCodes> {
   try {
     await execAsync(COMMANDS.GIT_CHECK);
+    // Verify Git is executable
+    await execAsync('git --version');
     return GitKeyKitCodes.SUCCESS;
   } catch (error) {
+    if (error instanceof Error && error.message.includes('not found')) {
+      return GitKeyKitCodes.ERR_GIT_NOT_FOUND;
+    }
-    return GitKeyKitCodes.ERR_GIT_NOT_FOUND;
+    return GitKeyKitCodes.ERR_GIT_CHECK_FAILED;
   }
 }
.github/workflows/release.yml (1)

27-27: Fix YAML formatting issues

Address the following formatting issues:

  1. Remove trailing spaces on line 27
  2. Add a newline at the end of the file

Also applies to: 32-32

🧰 Tools
🪛 yamllint (1.35.1)

[error] 27-27: trailing spaces

(trailing-spaces)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 624438e and d632deb.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (17)
  • .github/workflows/release.yml (1 hunks)
  • bin/index.ts (1 hunks)
  • package.json (2 hunks)
  • release.config.cjs (1 hunks)
  • src/commands/_start.txt (0 hunks)
  • src/commands/import.ts (1 hunks)
  • src/commands/reset.ts (2 hunks)
  • src/commands/start.ts (1 hunks)
  • src/systemCheck.ts (1 hunks)
  • src/utils/checkDependencies.ts (1 hunks)
  • src/utils/checkSecretKeys.ts (1 hunks)
  • src/utils/configureGPG.ts (0 hunks)
  • src/utils/createKey.ts (1 hunks)
  • src/utils/extractKey.ts (0 hunks)
  • src/utils/generate.ts (0 hunks)
  • src/utils/linuxConfig.ts (1 hunks)
  • src/utils/setGitConfig.ts (1 hunks)
💤 Files with no reviewable changes (4)
  • src/commands/_start.txt
  • src/utils/extractKey.ts
  • src/utils/generate.ts
  • src/utils/configureGPG.ts
✅ Files skipped from review due to trivial changes (4)
  • src/commands/import.ts
  • src/commands/reset.ts
  • release.config.cjs
  • bin/index.ts
🧰 Additional context used
🪛 yamllint (1.35.1)
.github/workflows/release.yml

[error] 27-27: trailing spaces

(trailing-spaces)


[error] 32-32: no new line character at the end of file

(new-line-at-end-of-file)

🔇 Additional comments (4)
src/commands/start.ts (1)

13-52: Well-structured initialization with comprehensive error handling

The start function is well-organized, with clear steps for checking dependencies, creating keys, configuring Git, and adding extra configuration for non-Windows platforms. Error handling is appropriately managed at each step, ensuring robust execution.

src/utils/linuxConfig.ts (1)

60-65: Assess the necessity of restarting the GPG agent

Forcefully killing and restarting the gpg-agent may disrupt other processes depending on it. Confirm that this action is necessary and consider notifying the user before proceeding.

Alternatively, you might include a check or prompt to ensure that restarting the agent won't adversely affect the user's environment.

src/systemCheck.ts (1)

56-69: LGTM! Well-structured orchestration function

The function properly orchestrates the dependency checks in a logical sequence with clear error handling and return values.

package.json (1)

31-32: LGTM! Proper semantic-release integration

The semantic-release script and dependency are properly integrated with consistent versioning across the workflow.

Also applies to: 48-48

Comment on lines +13 to +18
gpgProcess.stdout.on('data', (data: Buffer) => {
const output = data.toString();
if (output.includes('sec')) {
foundSecretKey = true;
}
logger.green("Setup finished! Happy coding!");
} else {
logger.warning("No secret keys found on your system.");
const ok = await confirm({ message: "Do you want to generate GPG keys now?" });
if (ok) {
await generateGpgKeys();
await setGitConfig(gpgAgentAddress);
logger.highlight("Before config");
if (platform === "linux") {
await configureGPG();
logger.green("Setup finished! Happy coding!");
process.exit(1);
}
logger.green("Setup finished! Happy coding!");
process.exit(1);
} else {
process.exit(1);
}
}
} catch (error: any) {
logger.error("Error:", (error as Error).message);
}
}
});
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Accumulate stdout data to ensure complete output is processed

Currently, the code checks for 'sec' in each data chunk, which might miss the keyword if it's split across chunks. Consider accumulating the data and processing it after the GPG process has closed to ensure accurate detection.

Apply this diff to adjust the data handling:

     let foundSecretKey = false;
+    let output = '';

     gpgProcess.stdout.on('data', (data: Buffer) => {
-      const output = data.toString();
-      if (output.includes('sec')) {
-        foundSecretKey = true;
-      }
+      output += data.toString();
     });

     gpgProcess.on('error', () => {
       resolve(GitKeyKitCodes.ERR_NO_SECRET_KEYS);
     });

     gpgProcess.on('close', () => {
-      resolve(foundSecretKey ? 
+      if (output.includes('sec')) {
+        resolve(GitKeyKitCodes.SUCCESS);
+      } else {
         GitKeyKitCodes.ERR_NO_SECRET_KEYS
+      }
     });

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +45 to +46
await writeFile(gpgConfPath, "");
} catch (error) {
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Avoid overwriting existing GPG configuration

The writeFile call overwrites the existing gpg.conf, which may erase user's configurations. Instead, remove this step and allow appendToFile to create the file if it doesn't exist.

Apply this diff to prevent overwriting:

     try {
-      await writeFile(gpgConfPath, "");
     } catch (error) {
       console.error(chalk.red("Error: Could not open gpg.conf"));
       return GitKeyKitCodes.ERR_INVALID_INPUT;
     }
📝 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.

Suggested change
await writeFile(gpgConfPath, "");
} catch (error) {
try {
} catch (error) {


async function setGitConfigValue(key: string, value: string): Promise<void> {
try {
await execAsync(`git config --global ${key} "${value}"`);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Prevent potential command injection by sanitizing inputs

The value parameter is directly interpolated into a shell command, which can lead to command injection if value contains special characters. Consider sanitizing the value or using a safer method to execute the command.

You can use the execFile method from the child_process module, which avoids shell interpolation by accepting arguments as an array.

Apply this diff to refactor the function:

-import { exec } from 'child_process';
+import { execFile } from 'child_process';

-import { promisify } from 'util';
-const execAsync = promisify(exec);
+const execFileAsync = promisify(execFile);

 async function setGitConfigValue(key: string, value: string): Promise<void> {
   try {
-    await execAsync(`git config --global ${key} "${value}"`);
+    await execFileAsync('git', ['config', '--global', key, value]);
   } catch (error) {
     throw new Error(`Error setting git config ${key}`);
   }
 }

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +11 to +26
const { stdout } = await execAsync('gpg --list-secret-keys');

// Find the longest string that could be a fingerprint
const lines = stdout.split('\n');
let maxLength = 0;
let keyFingerprint = '';

logger.log("Setting up your key");
const gpgLog: string = execSync("gpg --list-secret-keys").toString();
const keyID: string | null = extractKey(gpgLog);
for (const line of lines) {
const tokens = line.trim().split(/\s+/);
for (const token of tokens) {
if (token.length > maxLength) {
keyFingerprint = token;
maxLength = token.length;
}
}
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Improve the method for extracting GPG key fingerprint

The current method of finding the GPG key fingerprint by selecting the longest token in the output of gpg --list-secret-keys is not robust and may fail if the output format changes or contains unexpected data. Consider using the --with-colons option to get machine-readable output and parse it reliably.

You can refactor the function to use gpg --list-secret-keys --with-colons and parse the 'fpr' (fingerprint) lines to extract the fingerprint.

Apply this diff to improve the function:

 async function getGpgKeyFingerprint(): Promise<string> {
   try {
-    const { stdout } = await execAsync('gpg --list-secret-keys');
+    const { stdout } = await execAsync('gpg --list-secret-keys --with-colons');

-    // Find the longest string that could be a fingerprint
-    const lines = stdout.split('\n');
-    let maxLength = 0;
-    let keyFingerprint = '';

-    for (const line of lines) {
-      const tokens = line.trim().split(/\s+/);
-      for (const token of tokens) {
-        if (token.length > maxLength) {
-          keyFingerprint = token;
-          maxLength = token.length;
-        }
-      }
-    }

+    const lines = stdout.split('\n');
+    let keyFingerprint = '';
+    for (const line of lines) {
+      const parts = line.split(':');
+      if (parts[0] === 'fpr') {
+        keyFingerprint = parts[9];
+        break;
+      }
+    }

     if (!keyFingerprint) {
       throw new Error('No GPG key found');
     }

     console.log(chalk.green(`Found GPG key: ${keyFingerprint}`));
     return keyFingerprint;

   } catch (error) {
     throw new Error('Failed to get GPG key fingerprint');
   }
 }
📝 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.

Suggested change
const { stdout } = await execAsync('gpg --list-secret-keys');
// Find the longest string that could be a fingerprint
const lines = stdout.split('\n');
let maxLength = 0;
let keyFingerprint = '';
logger.log("Setting up your key");
const gpgLog: string = execSync("gpg --list-secret-keys").toString();
const keyID: string | null = extractKey(gpgLog);
for (const line of lines) {
const tokens = line.trim().split(/\s+/);
for (const token of tokens) {
if (token.length > maxLength) {
keyFingerprint = token;
maxLength = token.length;
}
}
}
const { stdout } = await execAsync('gpg --list-secret-keys --with-colons');
const lines = stdout.split('\n');
let keyFingerprint = '';
for (const line of lines) {
const parts = line.split(':');
if (parts[0] === 'fpr') {
keyFingerprint = parts[9];
break;
}
}

Comment on lines +22 to +35
const gpg = spawn("gpg", ["--full-generate-key"], {
stdio: "inherit",
});

gpg.on("close", (code) => {
if (code === 0) {
console.log(chalk.green("GPG key has been generated successfully."));
resolve(GitKeyKitCodes.SUCCESS);
} else {
console.error(chalk.red("Error: Failed to generate GPG key."));
resolve(GitKeyKitCodes.ERR_KEY_GENERATION);
}
});
});
Copy link

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 the spawned process to cover unexpected errors

Currently, the child process gpg only handles the 'close' event. If the process fails to start or encounters an error, the 'error' event should be handled to catch these cases and prevent the promise from hanging.

You can add an error handler as follows:

 const gpg = spawn("gpg", ["--full-generate-key"], {
   stdio: "inherit",
 });

+  gpg.on("error", (error) => {
+    console.error(chalk.red(`Failed to start GPG process: ${error.message}`));
+    resolve(GitKeyKitCodes.ERR_KEY_GENERATION);
+  });

   gpg.on("close", (code) => {
     if (code === 0) {
       console.log(chalk.green("GPG key has been generated successfully."));
       resolve(GitKeyKitCodes.SUCCESS);
     } else {
       console.error(chalk.red("Error: Failed to generate GPG key."));
       resolve(GitKeyKitCodes.ERR_KEY_GENERATION);
     }
   });
📝 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.

Suggested change
const gpg = spawn("gpg", ["--full-generate-key"], {
stdio: "inherit",
});
gpg.on("close", (code) => {
if (code === 0) {
console.log(chalk.green("GPG key has been generated successfully."));
resolve(GitKeyKitCodes.SUCCESS);
} else {
console.error(chalk.red("Error: Failed to generate GPG key."));
resolve(GitKeyKitCodes.ERR_KEY_GENERATION);
}
});
});
const gpg = spawn("gpg", ["--full-generate-key"], {
stdio: "inherit",
});
gpg.on("error", (error) => {
console.error(chalk.red(`Failed to start GPG process: ${error.message}`));
resolve(GitKeyKitCodes.ERR_KEY_GENERATION);
});
gpg.on("close", (code) => {
if (code === 0) {
console.log(chalk.green("GPG key has been generated successfully."));
resolve(GitKeyKitCodes.SUCCESS);
} else {
console.error(chalk.red("Error: Failed to generate GPG key."));
resolve(GitKeyKitCodes.ERR_KEY_GENERATION);
}
});
});

Comment on lines +17 to +37
export async function checkGpgInstallation(): Promise<{ code: GitKeyKitCodes, path?: string }> {
try {
const { stdout } = await execAsync(COMMANDS.GPG_CHECK);
const gpgPath = stdout.trim();

if (gpgPath) {
return {
code: GitKeyKitCodes.SUCCESS,
path: gpgPath
};
}

return {
code: GitKeyKitCodes.ERR_GPG_NOT_FOUND
};
} catch (error) {
return {
code: GitKeyKitCodes.ERR_GPG_NOT_FOUND
};
}
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Enhance error handling and path validation

The error handling could be more specific and include path validation:

  1. Distinguish between different types of errors (e.g., command not found vs execution error)
  2. Validate the GPG path exists and is executable
 export async function checkGpgInstallation(): Promise<{ code: GitKeyKitCodes, path?: string }> {
   try {
     const { stdout } = await execAsync(COMMANDS.GPG_CHECK);
     const gpgPath = stdout.trim();
     
     if (gpgPath) {
+      try {
+        // Verify GPG is executable
+        await execAsync(`"${gpgPath}" --version`);
         return {
           code: GitKeyKitCodes.SUCCESS,
           path: gpgPath
         };
+      } catch (error) {
+        return {
+          code: GitKeyKitCodes.ERR_GPG_NOT_EXECUTABLE
+        };
+      }
     }
     
     return {
       code: GitKeyKitCodes.ERR_GPG_NOT_FOUND
     };
   } catch (error) {
+    if (error instanceof Error && error.message.includes('not found')) {
+      return {
+        code: GitKeyKitCodes.ERR_GPG_NOT_FOUND
+      };
+    }
     return {
-      code: GitKeyKitCodes.ERR_GPG_NOT_FOUND
+      code: GitKeyKitCodes.ERR_GPG_CHECK_FAILED
     };
   }
 }
📝 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.

Suggested change
export async function checkGpgInstallation(): Promise<{ code: GitKeyKitCodes, path?: string }> {
try {
const { stdout } = await execAsync(COMMANDS.GPG_CHECK);
const gpgPath = stdout.trim();
if (gpgPath) {
return {
code: GitKeyKitCodes.SUCCESS,
path: gpgPath
};
}
return {
code: GitKeyKitCodes.ERR_GPG_NOT_FOUND
};
} catch (error) {
return {
code: GitKeyKitCodes.ERR_GPG_NOT_FOUND
};
}
}
export async function checkGpgInstallation(): Promise<{ code: GitKeyKitCodes, path?: string }> {
try {
const { stdout } = await execAsync(COMMANDS.GPG_CHECK);
const gpgPath = stdout.trim();
if (gpgPath) {
try {
// Verify GPG is executable
await execAsync(`"${gpgPath}" --version`);
return {
code: GitKeyKitCodes.SUCCESS,
path: gpgPath
};
} catch (error) {
return {
code: GitKeyKitCodes.ERR_GPG_NOT_EXECUTABLE
};
}
}
return {
code: GitKeyKitCodes.ERR_GPG_NOT_FOUND
};
} catch (error) {
if (error instanceof Error && error.message.includes('not found')) {
return {
code: GitKeyKitCodes.ERR_GPG_NOT_FOUND
};
}
return {
code: GitKeyKitCodes.ERR_GPG_CHECK_FAILED
};
}
}

Comment on lines +14 to +32
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- uses: pnpm/action-setup@v4
with:
version: latest
- run: pnpm install --frozen-lockfile
- run: pnpm run build

- name: Release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
run: npm run semantic-release
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Enhance workflow security and reliability

Consider these improvements to the workflow:

  1. Add concurrency control to prevent parallel releases
  2. Pin the semantic-release version
  3. Add timeout limits
  4. Consider caching dependencies
+concurrency:
+  group: release-${{ github.ref }}
+  cancel-in-progress: true
+
 jobs:
   release:
     runs-on: ubuntu-latest
+    timeout-minutes: 15
     steps:
       - uses: actions/checkout@v4
+        with:
+          fetch-depth: 0
       - uses: actions/setup-node@v4
         with:
           node-version: 20
+          cache: 'pnpm'
       - uses: pnpm/action-setup@v4
         with:
           version: latest
       - run: pnpm install --frozen-lockfile
       - run: pnpm run build
       
       - name: Release
         env:
           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
           NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
-        run: npm run semantic-release
+        run: npx semantic-release@24.2.1
📝 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.

Suggested change
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- uses: pnpm/action-setup@v4
with:
version: latest
- run: pnpm install --frozen-lockfile
- run: pnpm run build
- name: Release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
run: npm run semantic-release
concurrency:
group: release-${{ github.ref }}
cancel-in-progress: true
jobs:
release:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'pnpm'
- uses: pnpm/action-setup@v4
with:
version: latest
- run: pnpm install --frozen-lockfile
- run: pnpm run build
- name: Release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
run: npx semantic-release@24.2.1
🧰 Tools
🪛 yamllint (1.35.1)

[error] 27-27: trailing spaces

(trailing-spaces)


[error] 32-32: no new line character at the end of file

(new-line-at-end-of-file)

Copy link

🎉 This PR is included in version 2.1.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant