UNPKG

n8n-nodes-base

Version:

Base nodes of n8n

646 lines 28.2 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Git = void 0; const config_1 = require("@n8n/config"); const di_1 = require("@n8n/di"); const promises_1 = require("fs/promises"); const n8n_workflow_1 = require("n8n-workflow"); const simple_git_1 = __importDefault(require("simple-git")); const url_1 = require("url"); const descriptions_1 = require("./descriptions"); const GenericFunctions_1 = require("./GenericFunctions"); class Git { description = { displayName: 'Git', name: 'git', icon: 'file:git.svg', group: ['transform'], version: [1, 1.1], description: 'Control git.', defaults: { name: 'Git', }, usableAsTool: true, inputs: [n8n_workflow_1.NodeConnectionTypes.Main], outputs: [n8n_workflow_1.NodeConnectionTypes.Main], credentials: [ { name: 'gitPassword', required: true, displayOptions: { show: { authentication: ['gitPassword'], }, }, }, ], properties: [ { displayName: 'Authentication', name: 'authentication', type: 'options', options: [ { name: 'Authenticate', value: 'gitPassword', }, { name: 'None', value: 'none', }, ], displayOptions: { show: { operation: ['clone', 'push'], }, }, default: 'none', description: 'The way to authenticate', }, { displayName: 'Operation', name: 'operation', type: 'options', noDataExpression: true, default: 'log', options: [ { name: 'Add', value: 'add', description: 'Add a file or folder to commit', action: 'Add a file or folder to commit', }, { name: 'Add Config', value: 'addConfig', description: 'Add configuration property', action: 'Add configuration property', }, { name: 'Clone', value: 'clone', description: 'Clone a repository', action: 'Clone a repository', }, { name: 'Commit', value: 'commit', description: 'Commit files or folders to git', action: 'Commit files or folders to git', }, { name: 'Fetch', value: 'fetch', description: 'Fetch from remote repository', action: 'Fetch from remote repository', }, { name: 'List Config', value: 'listConfig', description: 'Return current configuration', action: 'Return current configuration', }, { name: 'Log', value: 'log', description: 'Return git commit history', action: 'Return git commit history', }, { name: 'Pull', value: 'pull', description: 'Pull from remote repository', action: 'Pull from remote repository', }, { name: 'Push', value: 'push', description: 'Push to remote repository', action: 'Push to remote repository', }, { name: 'Push Tags', value: 'pushTags', description: 'Push Tags to remote repository', action: 'Push tags to remote repository', }, { name: 'Reflog', value: 'reflog', description: 'Return reference log', action: 'Return reference log', }, { name: 'Status', value: 'status', description: 'Return status of current repository', action: 'Return status of current repository', }, { name: 'Switch Branch', value: 'switchBranch', description: 'Switch to a different branch', action: 'Switch to a different branch', }, { name: 'Tag', value: 'tag', description: 'Create a new tag', action: 'Create a new tag', }, { name: 'User Setup', value: 'userSetup', description: 'Set the user', action: 'Set up a user', }, ], }, { displayName: 'Repository Path', name: 'repositoryPath', type: 'string', displayOptions: { hide: { operation: ['clone'], }, }, default: '', placeholder: '/tmp/repository', required: true, description: 'Local path of the git repository to operate on', }, { displayName: 'New Repository Path', name: 'repositoryPath', type: 'string', displayOptions: { show: { operation: ['clone'], }, }, default: '', placeholder: '/tmp/repository', required: true, description: 'Local path to which the git repository should be cloned into', }, ...descriptions_1.addFields, ...descriptions_1.addConfigFields, ...descriptions_1.cloneFields, ...descriptions_1.commitFields, ...descriptions_1.logFields, ...descriptions_1.pushFields, ...descriptions_1.reflogFields, ...descriptions_1.switchBranchFields, ...descriptions_1.tagFields, // ...userSetupFields, ], }; async execute() { const items = this.getInputData(); const prepareRepository = async (repositoryPath) => { const authentication = this.getNodeParameter('authentication', 0); if (authentication === 'gitPassword') { const gitCredentials = await this.getCredentials('gitPassword'); const url = new url_1.URL(repositoryPath); url.username = gitCredentials.username; url.password = gitCredentials.password; return url.toString(); } return repositoryPath; }; const checkoutBranch = async (git, options) => { const { branchName, createBranch = true, startPoint, force = false, setUpstream = false, remoteName = 'origin', } = options; try { if (force) { await git.checkout(['-f', branchName]); } else { await git.checkout(branchName); } } catch (error) { if (createBranch) { // Try to create the branch when checkout fails if (startPoint) { await git.checkoutBranch(branchName, startPoint); } else { await git.checkoutLocalBranch(branchName); } // If we reach here, branch creation succeeded } else { // Don't create branch, throw original error throw error; } } if (setUpstream) { try { await git.addConfig(`branch.${branchName}.remote`, remoteName); await git.addConfig(`branch.${branchName}.merge`, `refs/heads/${branchName}`); } catch (upstreamError) { // Upstream setup failed but that's non-fatal } } }; const operation = this.getNodeParameter('operation', 0); const returnItems = []; for (let itemIndex = 0; itemIndex < items.length; itemIndex++) { try { const repositoryPath = this.getNodeParameter('repositoryPath', itemIndex, ''); const resolvedRepositoryPath = await this.helpers.resolvePath(repositoryPath); const isFilePathBlocked = this.helpers.isFilePathBlocked(resolvedRepositoryPath); if (isFilePathBlocked) { throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Access to the repository path is not allowed'); } const options = this.getNodeParameter('options', itemIndex, {}); if (operation === 'clone') { // Create repository folder if it does not exist try { await (0, promises_1.access)(resolvedRepositoryPath); } catch (error) { await (0, promises_1.mkdir)(resolvedRepositoryPath); } } const gitConfig = []; const deploymentConfig = di_1.Container.get(config_1.DeploymentConfig); const isCloud = deploymentConfig.type === 'cloud'; const securityConfig = di_1.Container.get(config_1.SecurityConfig); const disableBareRepos = securityConfig.disableBareRepos; if (isCloud || disableBareRepos) { gitConfig.push('safe.bareRepository=explicit'); } const enableHooks = securityConfig.enableGitNodeHooks; if (!enableHooks) { gitConfig.push('core.hooksPath=/dev/null'); } const gitOptions = { baseDir: resolvedRepositoryPath, config: gitConfig, }; const git = (0, simple_git_1.default)(gitOptions) // Tell git not to ask for any information via the terminal like for // example the username. As nobody will be able to answer it would // n8n keep on waiting forever. .env('GIT_TERMINAL_PROMPT', '0'); if (operation === 'add') { // ---------------------------------- // add // ---------------------------------- const pathsToAdd = this.getNodeParameter('pathsToAdd', itemIndex, ''); const paths = pathsToAdd .split(',') .map((p) => p.trim()) .filter((p) => p.length > 0); // Use -- separator to prevent argument injection await git.add(['--', ...paths]); returnItems.push({ json: { success: true, }, pairedItem: { item: itemIndex, }, }); } else if (operation === 'addConfig') { // ---------------------------------- // addConfig // ---------------------------------- const key = this.getNodeParameter('key', itemIndex, ''); const value = this.getNodeParameter('value', itemIndex, ''); const securityConfig = di_1.Container.get(config_1.SecurityConfig); const enableGitNodeAllConfigKeys = securityConfig.enableGitNodeAllConfigKeys; let append = false; if (!enableGitNodeAllConfigKeys && !descriptions_1.ALLOWED_CONFIG_KEYS.includes(key)) { throw new n8n_workflow_1.NodeOperationError(this.getNode(), `The provided git config key '${key}' is not allowed`); } if (options.mode === 'append') { append = true; } await git.addConfig(key, value, append); returnItems.push({ json: { success: true, }, pairedItem: { item: itemIndex, }, }); } else if (operation === 'clone') { // ---------------------------------- // clone // ---------------------------------- let sourceRepository = this.getNodeParameter('sourceRepository', itemIndex, ''); sourceRepository = await prepareRepository(sourceRepository); await git.clone(sourceRepository, '.'); returnItems.push({ json: { success: true, }, pairedItem: { item: itemIndex, }, }); } else if (operation === 'commit') { // ---------------------------------- // commit // ---------------------------------- const message = this.getNodeParameter('message', itemIndex, ''); const branch = options.branch; if (branch !== undefined && branch !== '') { (0, n8n_workflow_1.assertParamIsString)('branch', branch, this.getNode()); await checkoutBranch(git, { branchName: branch, setUpstream: true, }); } let pathsToAdd = undefined; if (options.files !== undefined) { pathsToAdd = options.pathsToAdd .split(',') .map((p) => p.trim()) .filter((p) => p.length > 0); } // Use -- separator to prevent argument injection if (pathsToAdd && pathsToAdd.length > 0) { await git.commit(message, ['--', ...pathsToAdd]); } else { await git.commit(message); } returnItems.push({ json: { success: true, }, pairedItem: { item: itemIndex, }, }); } else if (operation === 'fetch') { // ---------------------------------- // fetch // ---------------------------------- await git.fetch(); returnItems.push({ json: { success: true, }, pairedItem: { item: itemIndex, }, }); } else if (operation === 'log') { // ---------------------------------- // log // ---------------------------------- const logOptions = {}; const returnAll = this.getNodeParameter('returnAll', itemIndex, false); if (!returnAll) { logOptions.maxCount = this.getNodeParameter('limit', itemIndex, 100); } if (options.file) { logOptions.file = options.file; } const log = await git.log(logOptions); returnItems.push( // @ts-ignore ...this.helpers.returnJsonArray(log.all).map((item) => { return { ...item, pairedItem: { item: itemIndex }, }; })); } else if (operation === 'pull') { // ---------------------------------- // pull // ---------------------------------- await git.pull(); returnItems.push({ json: { success: true, }, pairedItem: { item: itemIndex, }, }); } else if (operation === 'push') { // ---------------------------------- // push // ---------------------------------- const branch = options.branch; if (branch !== undefined && branch !== '') { (0, n8n_workflow_1.assertParamIsString)('branch', branch, this.getNode()); await checkoutBranch(git, { branchName: branch, createBranch: false, setUpstream: true, }); } if (options.repository) { const targetRepository = await prepareRepository(options.targetRepository); await git.push(targetRepository); } else { const authentication = this.getNodeParameter('authentication', 0); if (authentication === 'gitPassword') { // Try to get remote repository path from git repository itself to add // authentication data const config = await git.listConfig(); let targetRepository; for (const fileName of Object.keys(config.values)) { if (config.values[fileName]['remote.origin.url']) { targetRepository = config.values[fileName]['remote.origin.url']; break; } } targetRepository = await prepareRepository(targetRepository); await git.push(targetRepository); } else { await git.push(); } } returnItems.push({ json: { success: true, }, pairedItem: { item: itemIndex, }, }); } else if (operation === 'pushTags') { // ---------------------------------- // pushTags // ---------------------------------- await git.pushTags(); returnItems.push({ json: { success: true, }, pairedItem: { item: itemIndex, }, }); } else if (operation === 'reflog') { // ---------------------------------- // reflog // ---------------------------------- const returnAll = this.getNodeParameter('returnAll', itemIndex, false); let reference = 'HEAD'; if (options.reference !== undefined && options.reference !== '') { (0, n8n_workflow_1.assertParamIsString)('reference', options.reference, this.getNode()); (0, GenericFunctions_1.validateGitReference)(options.reference, this.getNode()); reference = options.reference; } const reflogResult = await git.raw(['reflog', reference]); const reflogEntries = reflogResult .trim() .split('\n') .filter((line) => line.length > 0) .map((line) => { // reflog format: hash ref@{number}: action: message const match = line.match(/^(\S+)\s+(.+?):\s+(.+?):\s+(.+)$/); if (match) { return { hash: match[1], ref: match[2], action: match[3], message: match[4], raw: line, }; } return { raw: line, }; }); const entries = returnAll ? reflogEntries : reflogEntries.slice(0, this.getNodeParameter('limit', itemIndex, 100)); returnItems.push.apply(returnItems, this.helpers.returnJsonArray(entries).map((item) => { return { ...item, pairedItem: { item: itemIndex }, }; })); } else if (operation === 'listConfig') { // ---------------------------------- // listConfig // ---------------------------------- const config = await git.listConfig(); const data = (0, GenericFunctions_1.mapGitConfigList)(config); returnItems.push(...this.helpers.returnJsonArray(data).map((item) => { return { ...item, pairedItem: { item: itemIndex }, }; })); } else if (operation === 'status') { // ---------------------------------- // status // ---------------------------------- const status = await git.status(); returnItems.push( // @ts-ignore ...this.helpers.returnJsonArray([status]).map((item) => { return { ...item, pairedItem: { item: itemIndex }, }; })); } else if (operation === 'switchBranch') { // ---------------------------------- // switchBranch // ---------------------------------- const branchName = this.getNodeParameter('branchName', itemIndex); (0, n8n_workflow_1.assertParamIsString)('branchName', branchName, this.getNode()); const createBranch = options.createBranch; if (createBranch !== undefined) { (0, n8n_workflow_1.assertParamIsBoolean)('createBranch', createBranch, this.getNode()); } const remoteName = typeof options.remoteName === 'string' && options.remoteName ? options.remoteName : 'origin'; const startPoint = options.startPoint; if (startPoint !== undefined) { (0, n8n_workflow_1.assertParamIsString)('startPoint', startPoint, this.getNode()); } const setUpstream = options.setUpstream; if (setUpstream !== undefined) { (0, n8n_workflow_1.assertParamIsBoolean)('setUpstream', setUpstream, this.getNode()); } const force = options.force; if (force !== undefined) { (0, n8n_workflow_1.assertParamIsBoolean)('force', force, this.getNode()); } await checkoutBranch(git, { branchName, createBranch, startPoint, force, setUpstream, remoteName, }); returnItems.push({ json: { success: true, branch: branchName, }, pairedItem: { item: itemIndex, }, }); } else if (operation === 'tag') { // ---------------------------------- // tag // ---------------------------------- const name = this.getNodeParameter('name', itemIndex, ''); await git.addTag(name); returnItems.push({ json: { success: true, }, pairedItem: { item: itemIndex, }, }); } } catch (error) { if (this.continueOnFail()) { returnItems.push({ json: { error: error.toString(), }, pairedItem: { item: itemIndex, }, }); continue; } throw error; } } return [returnItems]; } } exports.Git = Git; //# sourceMappingURL=Git.node.js.map