UNPKG

@armyknife/backstage-github-template

Version:

GitHub repository template creation for Backstage

172 lines (171 loc) 8.19 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.createGitHubRepoAction = void 0; const plugin_scaffolder_backend_1 = require("@backstage/plugin-scaffolder-backend"); const rest_1 = require("@octokit/rest"); function createGitHubRepoAction(options) { const { integrations, config, catalogClient, reader, logger } = options; return (0, plugin_scaffolder_backend_1.createTemplateAction)({ id: 'armyknife:github:create-repo', description: 'Creates a GitHub repository with security best practices', schema: { input: { type: 'object', required: ['name', 'owner', 'description'], properties: { name: { type: 'string', title: 'Repository Name', description: 'Name of the repository', }, owner: { type: 'string', title: 'Repository Owner', description: 'Owner of the repository (organization or user)', }, description: { type: 'string', title: 'Description', description: 'Description of the repository', }, visibility: { type: 'string', enum: ['public', 'private', 'internal'], default: 'private', title: 'Repository Visibility', description: 'Visibility of the repository', }, enableSecurityFeatures: { type: 'boolean', default: true, title: 'Enable Security Features', description: 'Enable security features like branch protection, CODEOWNERS, etc.', }, token: { type: 'string', title: 'GitHub Token', description: 'GitHub token to use for repository creation. If not provided, will use the token from app-config.yaml', }, defaultBranch: { type: 'string', title: 'Default Branch', description: 'Default branch for the repository', default: 'main', }, }, }, output: { type: 'object', properties: { remoteUrl: { type: 'string', title: 'Remote URL', description: 'URL to the repository', }, repoContentsUrl: { type: 'string', title: 'Repository Contents URL', description: 'URL to the repository contents', }, }, }, }, async handler(ctx) { const { name, owner, description, visibility = 'private', enableSecurityFeatures = true, token, defaultBranch = 'main', } = ctx.input; // Get GitHub token from input or config const githubToken = token || config.getOptionalString('integrations.github.token'); if (!githubToken) { throw new Error('No GitHub token provided in action input or app-config.yaml'); } // Initialize GitHub client const octokit = new rest_1.Octokit({ auth: githubToken, }); try { // Create repository const createRepoResponse = await octokit.repos.createInOrg({ org: owner, name, description, private: visibility !== 'public', auto_init: true, delete_branch_on_merge: true, allow_squash_merge: true, allow_merge_commit: false, allow_rebase_merge: true, }).catch(async (error) => { // If org creation fails, try as user if (error.status === 404) { return await octokit.repos.createForAuthenticatedUser({ name, description, private: visibility !== 'public', auto_init: true, delete_branch_on_merge: true, allow_squash_merge: true, allow_merge_commit: false, allow_rebase_merge: true, }); } throw error; }); const repoFullName = createRepoResponse.data.full_name; const repoUrl = createRepoResponse.data.html_url; const defaultBranchRef = `heads/${defaultBranch}`; ctx.logger.info(`Created GitHub repository: ${repoFullName}`); // If security features are enabled, set up branch protection if (enableSecurityFeatures) { try { // Add CODEOWNERS file await octokit.repos.createOrUpdateFileContents({ owner: createRepoResponse.data.owner.login, repo: name, path: '.github/CODEOWNERS', message: 'Add CODEOWNERS file', content: Buffer.from('# Default owners for everything\n* @' + owner).toString('base64'), branch: defaultBranch, }); // Add security policy await octokit.repos.createOrUpdateFileContents({ owner: createRepoResponse.data.owner.login, repo: name, path: 'SECURITY.md', message: 'Add security policy', content: Buffer.from('# Security Policy\n\n## Reporting a Vulnerability\n\nPlease report security vulnerabilities to security@example.com.').toString('base64'), branch: defaultBranch, }); // Set up branch protection await octokit.repos.updateBranchProtection({ owner: createRepoResponse.data.owner.login, repo: name, branch: defaultBranch, required_status_checks: { strict: true, contexts: ['continuous-integration/ci-checks'], }, enforce_admins: true, required_pull_request_reviews: { dismiss_stale_reviews: true, require_code_owner_reviews: true, required_approving_review_count: 1, }, restrictions: null, }); ctx.logger.info(`Set up security features for ${repoFullName}`); } catch (error) { ctx.logger.error(`Failed to set up security features: ${error.message}`); } } // Set output ctx.output('remoteUrl', repoUrl); ctx.output('repoContentsUrl', `${repoUrl}/blob/${defaultBranch}`); } catch (error) { ctx.logger.error(`Failed to create GitHub repository: ${error.message}`); throw error; } }, }); } exports.createGitHubRepoAction = createGitHubRepoAction;