UNPKG

qraft

Version:

A powerful CLI tool to qraft structured project setups from GitHub template repositories

348 lines 13.2 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.PermissionChecker = void 0; class PermissionChecker { constructor(githubToken) { this.githubToken = githubToken; } async checkRepositoryPermissions(repoOwner, repoName) { try { const repoInfo = await this.getRepositoryInfo(repoOwner, repoName); const permissions = await this.getUserPermissions(repoOwner, repoName); const alternatives = this.generateAlternatives(repoInfo, permissions); const recommendations = this.generateRecommendations(repoInfo, permissions); return { hasPermission: permissions.canWrite, permissions, alternatives, recommendations, requiresAuth: !this.githubToken }; } catch (error) { return this.handlePermissionError(error, repoOwner, repoName); } } async getRepositoryInfo(owner, name) { const url = `https://api.github.com/repos/${owner}/${name}`; const headers = { 'Accept': 'application/vnd.github.v3+json', 'User-Agent': 'qraft-cli' }; if (this.githubToken) { headers['Authorization'] = `token ${this.githubToken}`; } const response = await fetch(url, { headers }); if (!response.ok) { if (response.status === 404) { throw new Error(`Repository ${owner}/${name} not found or not accessible`); } if (response.status === 403) { throw new Error(`Access forbidden to repository ${owner}/${name}`); } throw new Error(`Failed to fetch repository info: ${response.statusText}`); } const data = await response.json(); return { owner: data.owner.login, name: data.name, fullName: data.full_name, isPublic: !data.private, isFork: data.fork, parentRepo: data.parent ? data.parent.full_name : undefined, defaultBranch: data.default_branch, permissions: data.permissions ? { canRead: data.permissions.pull, canWrite: data.permissions.push, canAdmin: data.permissions.admin, canCreatePR: data.permissions.pull, canFork: !data.private || data.permissions.pull, userRole: this.determineUserRole(data.permissions), isPublic: !data.private, isFork: data.fork } : undefined }; } async getUserPermissions(owner, name) { if (!this.githubToken) { // Without authentication, we can only access public repos for reading const repoInfo = await this.getRepositoryInfo(owner, name); return { canRead: repoInfo.isPublic, canWrite: false, canAdmin: false, canCreatePR: repoInfo.isPublic, canFork: repoInfo.isPublic, userRole: 'none', isPublic: repoInfo.isPublic, isFork: repoInfo.isFork }; } // Use the permissions from repository info if available const repoInfo = await this.getRepositoryInfo(owner, name); if (repoInfo.permissions) { return repoInfo.permissions; } // Fallback: try to determine permissions through API calls return await this.probePermissions(owner, name); } async probePermissions(owner, name) { const headers = { 'Authorization': `token ${this.githubToken}`, 'Accept': 'application/vnd.github.v3+json', 'User-Agent': 'qraft-cli' }; // Check if we can access the repo at all try { const repoResponse = await fetch(`https://api.github.com/repos/${owner}/${name}`, { headers }); const canRead = repoResponse.ok; if (!canRead) { return { canRead: false, canWrite: false, canAdmin: false, canCreatePR: false, canFork: false, userRole: 'none', isPublic: false, isFork: false }; } const repoData = await repoResponse.json(); // Try to check if we can create a branch (indicates write access) const branchResponse = await fetch(`https://api.github.com/repos/${owner}/${name}/git/refs/heads`, { headers }); const canWrite = branchResponse.ok; return { canRead: true, canWrite, canAdmin: false, // Would need more specific checks canCreatePR: true, canFork: !repoData.private, userRole: canWrite ? 'write' : 'read', isPublic: !repoData.private, isFork: repoData.fork }; } catch { return { canRead: false, canWrite: false, canAdmin: false, canCreatePR: false, canFork: false, userRole: 'none', isPublic: false, isFork: false }; } } determineUserRole(permissions) { if (permissions.admin) return 'admin'; if (permissions.maintain) return 'admin'; if (permissions.push) return 'write'; if (permissions.triage) return 'triage'; if (permissions.pull) return 'read'; return 'none'; } generateAlternatives(_repoInfo, permissions) { const alternatives = []; // If no write access, suggest forking if (!permissions.canWrite && permissions.canFork) { alternatives.push({ action: 'fork', description: 'Fork the repository to your account and create a pull request', automated: true, steps: [ 'Fork the repository to your GitHub account', 'Create the box in your forked repository', 'Create a pull request to the original repository' ] }); } // If can create PR but not write directly if (!permissions.canWrite && permissions.canCreatePR) { alternatives.push({ action: 'create_pr', description: 'Create a pull request with your changes', automated: true, steps: [ 'Create a new branch in the repository', 'Commit your box changes to the branch', 'Create a pull request for review' ] }); } // If no access at all, suggest requesting access if (!permissions.canRead && !permissions.canWrite) { alternatives.push({ action: 'request_access', description: 'Request access to the repository', automated: false, steps: [ 'Contact the repository owner', 'Request collaborator access', 'Wait for approval' ] }); } // Suggest using a different repository alternatives.push({ action: 'use_different_repo', description: 'Use a different repository where you have write access', automated: false, steps: [ 'Choose a repository where you have write permissions', 'Update your registry configuration', 'Create the box in the accessible repository' ] }); return alternatives; } generateRecommendations(repoInfo, permissions) { const recommendations = []; if (!this.githubToken) { recommendations.push('🔑 Set up GitHub authentication for better access control'); recommendations.push('Use: export GITHUB_TOKEN=your_token_here'); } if (!permissions.canWrite) { if (permissions.canFork) { recommendations.push('🍴 Fork the repository to contribute changes'); } else { recommendations.push('📝 Request collaborator access from the repository owner'); } } if (repoInfo.isFork && permissions.canWrite) { recommendations.push('🔄 Consider creating PR to upstream repository'); } if (permissions.userRole === 'read') { recommendations.push('📖 You have read-only access - use fork workflow for contributions'); } if (!permissions.canRead) { recommendations.push('🔒 Repository is private and you don\'t have access'); recommendations.push('Contact the owner to request access'); } return recommendations; } handlePermissionError(error, _owner, _name) { const errorMessage = error.message || 'Unknown error'; return { hasPermission: false, permissions: { canRead: false, canWrite: false, canAdmin: false, canCreatePR: false, canFork: false, userRole: 'none', isPublic: false, isFork: false }, alternatives: [ { action: 'use_different_repo', description: 'Use a different repository', automated: false, steps: [ 'Verify the repository exists and is accessible', 'Check your GitHub token permissions', 'Use a repository where you have access' ] } ], recommendations: [ `❌ Error: ${errorMessage}`, '🔍 Verify repository exists and is accessible', '🔑 Check your GitHub authentication' ], requiresAuth: !this.githubToken }; } // Utility method to validate repository URL format static parseRepositoryUrl(url) { // Handle various GitHub URL formats const patterns = [ /^https:\/\/github\.com\/([^/]+)\/([^/]+?)(?:\.git)?(?:\/.*)?$/, /^git@github\.com:([^/]+)\/([^/]+?)(?:\.git)?$/, /^([^/]+)\/([^/]+)$/ ]; for (const pattern of patterns) { const match = url.match(pattern); if (match) { return { owner: match[1], name: match[2] }; } } return null; } // Check if user has GitHub token configured hasGitHubToken() { return !!this.githubToken; } // Get current user info (requires token) async getCurrentUser() { if (!this.githubToken) return null; try { const response = await fetch('https://api.github.com/user', { headers: { 'Authorization': `token ${this.githubToken}`, 'Accept': 'application/vnd.github.v3+json', 'User-Agent': 'qraft-cli' } }); if (response.ok) { const data = await response.json(); return { login: data.login, name: data.name || data.login }; } } catch { // Ignore errors } return null; } // Test method for checking permissions without making actual API calls async checkPermissionsDryRun(repoOwner, repoName, mockPermissions) { const defaultPermissions = { canRead: true, canWrite: false, canAdmin: false, canCreatePR: true, canFork: true, userRole: 'read', isPublic: true, isFork: false, ...mockPermissions }; const mockRepoInfo = { owner: repoOwner, name: repoName, fullName: `${repoOwner}/${repoName}`, isPublic: defaultPermissions.isPublic, isFork: defaultPermissions.isFork, defaultBranch: 'main', permissions: defaultPermissions }; const alternatives = this.generateAlternatives(mockRepoInfo, defaultPermissions); const recommendations = this.generateRecommendations(mockRepoInfo, defaultPermissions); return { hasPermission: defaultPermissions.canWrite, permissions: defaultPermissions, alternatives, recommendations, requiresAuth: !this.githubToken }; } } exports.PermissionChecker = PermissionChecker; //# sourceMappingURL=permissionChecker.js.map