"use strict";
/*
 * Copyright © 2020 Atomist, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.resolveCredentialsPromise = exports.gitBranchCompatible = exports.toRepoTargetingParametersMaker = exports.toScalarProjectEditor = exports.toParametersListing = exports.JobRequiredParameter = exports.JobDescriptionParameter = exports.JobNameParameter = exports.MsgIdParameter = exports.DryRunParameter = exports.toCommandListenerInvocation = exports.CommandListenerExecutionInterruptError = exports.eventHandlerRegistrationToEvent = exports.commandHandlerRegistrationToCommand = exports.generatorRegistrationToCommand = exports.codeInspectionRegistrationToCommand = exports.codeTransformRegistrationToCommand = exports.TransformTag = exports.InspectionTag = exports.GeneratorTag = void 0;
const HandlerResult_1 = require("@atomist/automation-client/lib/HandlerResult");
const decoratorSupport_1 = require("@atomist/automation-client/lib/internal/metadata/decoratorSupport");
const onEvent_1 = require("@atomist/automation-client/lib/onEvent");
const GitHubRepoRef_1 = require("@atomist/automation-client/lib/operations/common/GitHubRepoRef");
const repoFilter_1 = require("@atomist/automation-client/lib/operations/common/repoFilter");
const repoUtils_1 = require("@atomist/automation-client/lib/operations/common/repoUtils");
const editAll_1 = require("@atomist/automation-client/lib/operations/edit/editAll");
const editModes_1 = require("@atomist/automation-client/lib/operations/edit/editModes");
const projectEditor_1 = require("@atomist/automation-client/lib/operations/edit/projectEditor");
const projectEditorOps_1 = require("@atomist/automation-client/lib/operations/edit/projectEditorOps");
const GitHubRepoCreationParameters_1 = require("@atomist/automation-client/lib/operations/generate/GitHubRepoCreationParameters");
const Project_1 = require("@atomist/automation-client/lib/project/Project");
const SmartParameters_1 = require("@atomist/automation-client/lib/SmartParameters");
const constructionUtils_1 = require("@atomist/automation-client/lib/util/constructionUtils");
const logger_1 = require("@atomist/automation-client/lib/util/logger");
const slack_messages_1 = require("@atomist/slack-messages");
const _ = require("lodash");
const GitHubRepoTargets_1 = require("../../api/command/target/GitHubRepoTargets");
const TransformModeSuggestion_1 = require("../../api/command/target/TransformModeSuggestion");
const parameterPrompt_1 = require("../../api/context/parameterPrompt");
const preferenceStore_1 = require("../../api/context/preferenceStore");
const skillContext_1 = require("../../api/context/skillContext");
const RepoTargets_1 = require("../../api/machine/RepoTargets");
const ParametersBuilder_1 = require("../../api/registration/ParametersBuilder");
const ParametersDefinition_1 = require("../../api/registration/ParametersDefinition");
const createCommand_1 = require("../command/createCommand");
const generatorCommand_1 = require("../command/generator/generatorCommand");
const chattyDryRunAwareEditor_1 = require("../command/transform/chattyDryRunAwareEditor");
const LoggingProgressLog_1 = require("../log/LoggingProgressLog");
const dateFormat_1 = require("../misc/dateFormat");
const createJob_1 = require("../misc/job/createJob");
const messages_1 = require("../misc/slack/messages");
const projectLoaderRepoLoader_1 = require("./projectLoaderRepoLoader");
const RepoTargetingParameters_1 = require("./RepoTargetingParameters");
const toMachineOptions_1 = require("./toMachineOptions");
exports.GeneratorTag = "generator";
exports.InspectionTag = "inspection";
exports.TransformTag = "transform";
function codeTransformRegistrationToCommand(sdm, ctr) {
    tagWith(ctr, exports.TransformTag);
    const mo = toMachineOptions_1.toMachineOptions(sdm);
    addDryRunParameters(ctr);
    addJobParameters(ctr);
    addParametersDefinedInBuilder(ctr);
    ctr.paramsMaker = toRepoTargetingParametersMaker(ctr.paramsMaker || SmartParameters_1.NoParameters, ctr.targets || mo.targets || GitHubRepoTargets_1.GitHubRepoTargets);
    const description = ctr.description || ctr.name;
    const asCommand = Object.assign(Object.assign({ description }, ctr), { listener: async (ci) => {
            ci.credentials = await resolveCredentialsPromise(ci.credentials);
            const targets = ci.parameters.targets;
            const vr = targets.bindAndValidate();
            if (RepoTargets_1.isValidationError(vr)) {
                return ci.addressChannels(messages_1.slackErrorMessage(`Code Transform`, `Invalid parameters to code transform ${slack_messages_1.italic(ci.commandName)}:

${vr.message}`, ci.context));
            }
            const repoFinder = !!ci.parameters.targets.repoRef
                ? () => Promise.resolve([ci.parameters.targets.repoRef])
                : ctr.repoFinder || toMachineOptions_1.toMachineOptions(sdm).repoFinder;
            const repoLoader = !!ctr.repoLoader
                ? ctr.repoLoader(ci.parameters)
                : projectLoaderRepoLoader_1.projectLoaderRepoLoader(mo.projectLoader, ci.credentials, false, ci.context);
            const concurrency = Object.assign({ maxConcurrent: 2, requiresJob: false }, (ctr.concurrency || {}));
            try {
                const ids = await repoUtils_1.relevantRepos(ci.context, repoFinder, repoFilter_1.andFilter(targets.test, ctr.repoFilter));
                const requiresJob = _.get(ci.parameters, "job.required", concurrency.requiresJob);
                if (ids.length > 1 || !!requiresJob) {
                    const params = Object.assign({}, ci.parameters);
                    params.targets.repos = undefined;
                    params.targets.repo = undefined;
                    delete params["job.name"];
                    delete params["job.description"];
                    delete params["job.required"];
                    await createJob_1.createJob({
                        name: _.get(ci.parameters, "job.name") || `CodeTransform/${ci.commandName}`,
                        command: ci.commandName,
                        parameters: ids.map(id => (Object.assign(Object.assign({}, params), { "targets": {
                                owner: id.owner,
                                repo: id.repo,
                                branch: id.branch,
                                sha: id.sha,
                            }, "job.required": false }))),
                        description: _.get(ci.parameters, "job.description") ||
                            `Running code transform ${slack_messages_1.italic(ci.commandName)} on ${ids.length} ${ids.length === 1 ? "repository" : "repositories"}`,
                        concurrentTasks: concurrency.maxConcurrent,
                    }, ci.context);
                }
                else {
                    const editMode = toEditModeOrFactory(ctr, ci);
                    const result = await editAll_1.editOne(ci.context, ci.credentials, chattyDryRunAwareEditor_1.chattyDryRunAwareEditor(ctr, toScalarProjectEditor(ctr.transform, toMachineOptions_1.toMachineOptions(sdm), ctr.projectTest)), editMode, ids[0], ci.parameters, repoLoader);
                    if (!!ctr.onTransformResults) {
                        await ctr.onTransformResults([result], Object.assign(Object.assign({}, ci), { progressLog: new LoggingProgressLog_1.LoggingProgressLog(ctr.name, "debug") }));
                    }
                    else if (!!result && !!result.error) {
                        const error = result.error;
                        return ci.addressChannels(messages_1.slackErrorMessage(`Code Transform`, `Code transform ${slack_messages_1.italic(ci.commandName)} failed${!!error.message ? ":\n\n" + slack_messages_1.codeBlock(error.message) : "."}`, ci.context));
                    }
                    else {
                        logger_1.logger.debug("No react function to react to result of code transformation '%s'", ctr.name);
                    }
                }
            }
            catch (e) {
                return ci.addressChannels(messages_1.slackErrorMessage(`Code Transform`, `Code transform ${slack_messages_1.italic(ci.commandName)} failed${!!e.message ? ":\n\n" + slack_messages_1.codeBlock(e.message) : "."}`, ci.context));
            }
        } });
    return commandHandlerRegistrationToCommand(sdm, asCommand);
}
exports.codeTransformRegistrationToCommand = codeTransformRegistrationToCommand;
function codeInspectionRegistrationToCommand(sdm, cir) {
    tagWith(cir, exports.InspectionTag);
    const mo = toMachineOptions_1.toMachineOptions(sdm);
    addJobParameters(cir);
    addParametersDefinedInBuilder(cir);
    cir.paramsMaker = toRepoTargetingParametersMaker(cir.paramsMaker || SmartParameters_1.NoParameters, cir.targets || mo.targets || GitHubRepoTargets_1.GitHubRepoTargets);
    const description = cir.description || cir.name;
    const asCommand = Object.assign(Object.assign({ description }, cir), { listener: async (ci) => {
            ci.credentials = await resolveCredentialsPromise(ci.credentials);
            const targets = ci.parameters.targets;
            const vr = targets.bindAndValidate();
            if (RepoTargets_1.isValidationError(vr)) {
                return ci.addressChannels(messages_1.slackErrorMessage(`Code Inspection`, `Invalid parameters to code inspection ${slack_messages_1.italic(ci.commandName)}:

${slack_messages_1.codeBlock(vr.message)}`, ci.context));
            }
            const action = async (p) => {
                if (!!cir.projectTest && !(await cir.projectTest(p))) {
                    return { repoId: p.id, result: undefined };
                }
                return {
                    repoId: p.id,
                    result: await cir.inspection(p, Object.assign(Object.assign({}, ci), { progressLog: new LoggingProgressLog_1.LoggingProgressLog(cir.name, "debug") })),
                };
            };
            const repoFinder = !!ci.parameters.targets.repoRef
                ? () => Promise.resolve([ci.parameters.targets.repoRef])
                : cir.repoFinder || toMachineOptions_1.toMachineOptions(sdm).repoFinder;
            const repoLoader = !!cir.repoLoader
                ? cir.repoLoader(ci.parameters)
                : projectLoaderRepoLoader_1.projectLoaderRepoLoader(mo.projectLoader, ci.parameters.targets.credentials, true, ci.context);
            const concurrency = Object.assign({ maxConcurrent: 2, requiresJob: false }, (cir.concurrency || {}));
            try {
                const ids = await repoUtils_1.relevantRepos(ci.context, repoFinder, repoFilter_1.andFilter(targets.test, cir.repoFilter));
                const requiresJob = _.get(ci.parameters, "job.required", concurrency.requiresJob);
                if (ids.length > 1 || !!requiresJob) {
                    const params = Object.assign({}, ci.parameters);
                    params.targets.repos = undefined;
                    params.targets.repo = undefined;
                    delete params["job.name"];
                    delete params["job.description"];
                    delete params["job.required"];
                    await createJob_1.createJob({
                        name: `CodeInspection/${ci.commandName}`,
                        command: ci.commandName,
                        parameters: ids.map(id => (Object.assign(Object.assign({}, params), { "targets": {
                                owner: id.owner,
                                repo: id.repo,
                                branch: id.branch,
                                sha: id.sha,
                            }, "job.required": false }))),
                        description: `Running code inspection ${slack_messages_1.italic(ci.commandName)} on ${ids.length} repositories`,
                    }, ci.context);
                }
                else {
                    const project = await repoLoader(ids[0]);
                    const result = await action(project, ci.parameters);
                    if (!!cir.onInspectionResults) {
                        await cir.onInspectionResults([result], ci);
                    }
                    else {
                        logger_1.logger.debug("No react function to react to results of code inspection '%s'", cir.name);
                    }
                }
            }
            catch (e) {
                return ci.addressChannels(messages_1.slackErrorMessage(`Code Inspection`, `Code Inspection ${slack_messages_1.italic(ci.commandName)} failed:

${slack_messages_1.codeBlock(e.message)}`, ci.context));
            }
        } });
    return commandHandlerRegistrationToCommand(sdm, asCommand);
}
exports.codeInspectionRegistrationToCommand = codeInspectionRegistrationToCommand;
/**
 * Tag the command details with the given tag if it isn't already
 * @param {Partial<CommandDetails>} e
 * @param {string} tag
 */
function tagWith(e, tag) {
    if (!e.tags) {
        e.tags = [];
    }
    if (typeof e.tags === "string") {
        e.tags = [e.tags];
    }
    if (!e.tags.includes(tag)) {
        e.tags.push(tag);
    }
}
function generatorRegistrationToCommand(sdm, e) {
    tagWith(e, exports.GeneratorTag);
    if (!e.paramsMaker) {
        e.paramsMaker = SmartParameters_1.NoParameters;
    }
    if (e.startingPoint && Project_1.isProject(e.startingPoint) && !e.startingPoint.id) {
        // TODO should probably be handled in automation-client
        e.startingPoint.id = new GitHubRepoRef_1.GitHubRepoRef("ignore", "this");
    }
    addParametersDefinedInBuilder(e);
    return () => generatorCommand_1.generatorCommand(sdm, () => toScalarProjectEditor(e.transform, toMachineOptions_1.toMachineOptions(sdm)), e.name, e.paramsMaker, e.fallbackTarget || GitHubRepoCreationParameters_1.GitHubRepoCreationParameters, e.startingPoint, e, // required because we redefine the afterAction
    e);
}
exports.generatorRegistrationToCommand = generatorRegistrationToCommand;
function commandHandlerRegistrationToCommand(sdm, c) {
    return () => createCommand_1.createCommand(sdm, toOnCommand(c), c.name, c.paramsMaker, c);
}
exports.commandHandlerRegistrationToCommand = commandHandlerRegistrationToCommand;
function eventHandlerRegistrationToEvent(sdm, e) {
    addParametersDefinedInBuilder(e);
    return () => onEvent_1.eventHandlerFrom(e.listener, e.paramsMaker || SmartParameters_1.NoParameters, e.subscription, e.name, e.description, e.tags);
}
exports.eventHandlerRegistrationToEvent = eventHandlerRegistrationToEvent;
class CommandListenerExecutionInterruptError extends Error {
    constructor(message) {
        super(message);
        this.message = message;
    }
}
exports.CommandListenerExecutionInterruptError = CommandListenerExecutionInterruptError;
function toOnCommand(c) {
    addParametersDefinedInBuilder(c);
    return sdm => async (context, parameters) => {
        const cli = toCommandListenerInvocation(c, context, parameters, toMachineOptions_1.toMachineOptions(sdm));
        cli.credentials = await resolveCredentialsPromise(cli.credentials);
        try {
            const result = await c.listener(cli);
            return !!result ? result : HandlerResult_1.Success;
        }
        catch (err) {
            if (err instanceof CommandListenerExecutionInterruptError) {
                return {
                    code: -1,
                };
            }
            else {
                logger_1.logger.error("Error executing command '%s': %s", cli.commandName, err.message);
                logger_1.logger.error(err.stack);
                return {
                    code: 1,
                    message: err.message,
                };
            }
        }
    };
}
function toCommandListenerInvocation(c, context, parameters, sdm) {
    // It may already be there
    let credentials = !!context ? context.credentials : undefined;
    let ids;
    if (generatorCommand_1.isSeedDrivenGeneratorParameters(parameters)) {
        credentials = parameters.target.credentials;
        ids = [parameters.target.repoRef];
    }
    else if (RepoTargetingParameters_1.isRepoTargetingParameters(parameters)) {
        credentials = parameters.targets.credentials;
        ids = !!parameters.targets.repoRef ? [parameters.targets.repoRef] : undefined;
    }
    if (!!sdm.credentialsResolver) {
        try {
            credentials = sdm.credentialsResolver.commandHandlerCredentials(context, ids ? ids[0] : undefined);
        }
        catch (e) {
            logger_1.logger.debug(`Failed to obtain credentials from credentialsResolver: ${e.message}`);
        }
    }
    let matches;
    if (c.intent instanceof RegExp) {
        matches = c.intent.exec(context.trigger.raw_message);
    }
    const addressChannels = (msg, opts) => context.messageClient.respond(msg, opts);
    const promptFor = sdm.parameterPromptFactory ? sdm.parameterPromptFactory(context) : parameterPrompt_1.NoParameterPrompt;
    const preferences = sdm.preferenceStoreFactory ? sdm.preferenceStoreFactory(context) : preferenceStore_1.NoPreferenceStore;
    const configuration = (context || {}).configuration;
    return {
        commandName: c.name,
        context,
        parameters,
        addressChannels,
        configuration,
        promptFor,
        preferences,
        credentials,
        ids,
        matches,
        skill: skillContext_1.createSkillContext(context),
    };
}
exports.toCommandListenerInvocation = toCommandListenerInvocation;
exports.DryRunParameter = {
    name: "dry-run",
    description: "Run Code Transform in dry run mode so that changes aren't committed to the repository",
    required: false,
    defaultValue: false,
    type: "boolean",
};
exports.MsgIdParameter = {
    name: "msgId",
    description: "Id of the code transform message",
    required: false,
    type: "string",
    displayable: false,
};
/**
 * Add the dryRun parameter into the list of parameters
 */
function addDryRunParameters(c) {
    const params = toParametersListing(c.parameters || {});
    params.parameters.push(exports.DryRunParameter, exports.MsgIdParameter);
    c.parameters = params;
}
exports.JobNameParameter = {
    name: "job.name",
    description: "Name of the job to create",
    required: false,
    type: "string",
    displayable: false,
};
exports.JobDescriptionParameter = {
    name: "job.description",
    description: "Description of the job to create",
    required: false,
    type: "string",
    displayable: false,
};
exports.JobRequiredParameter = {
    name: "job.required",
    description: "Is job required",
    required: false,
    type: "boolean",
};
/**
 * Add the job parameter into the list of parameters
 */
function addJobParameters(c) {
    const params = toParametersListing(c.parameters || {});
    params.parameters.push(exports.JobNameParameter, exports.JobDescriptionParameter, exports.JobRequiredParameter);
    c.parameters = params;
}
/**
 * Add to the existing ParametersMaker any parameters defined in the builder itself
 * @param {CommandHandlerRegistration<PARAMS>} c
 */
function addParametersDefinedInBuilder(c) {
    const oldMaker = c.paramsMaker || SmartParameters_1.NoParameters;
    if (!!c.parameters) {
        c.paramsMaker = () => {
            const paramsInstance = constructionUtils_1.toFactory(oldMaker)();
            const paramListing = toParametersListing(c.parameters);
            paramListing.parameters.forEach(p => {
                paramsInstance[p.name] = p.defaultValue;
                decoratorSupport_1.declareParameter(paramsInstance, p.name, p);
            });
            paramListing.mappedParameters.forEach(mp => decoratorSupport_1.declareMappedParameter(paramsInstance, mp.name, mp.uri, mp.required));
            paramListing.secrets.forEach(s => decoratorSupport_1.declareSecret(paramsInstance, s.name, s.uri));
            paramListing.values.forEach(v => decoratorSupport_1.declareValue(paramsInstance, v.name, { path: v.path, required: v.required, type: v.type }));
            return paramsInstance;
        };
    }
}
function isMappedParameterOrSecretDeclaration(x) {
    const maybe = x;
    return !!maybe && !!maybe.declarationType;
}
function isValueDeclaration(x) {
    const maybe = x;
    return !!maybe && maybe.path !== undefined && maybe.path !== null;
}
function isParametersListing(p) {
    const maybe = p;
    return maybe.parameters !== undefined && maybe.mappedParameters !== undefined;
}
function toParametersListing(p) {
    if (isParametersListing(p)) {
        return p;
    }
    const builder = new ParametersBuilder_1.ParametersBuilder();
    for (const name of Object.getOwnPropertyNames(p)) {
        const value = p[name];
        if (isMappedParameterOrSecretDeclaration(value)) {
            switch (value.declarationType) {
                case ParametersDefinition_1.DeclarationType.Mapped:
                    builder.addMappedParameters({ name, uri: value.uri, required: value.required });
                    break;
                case ParametersDefinition_1.DeclarationType.Secret:
                    builder.addSecrets({ name, uri: value.uri });
                    break;
            }
        }
        else if (isValueDeclaration(value)) {
            builder.addValues({ name, path: value.path, required: value.required, type: value.type });
        }
        else {
            builder.addParameters(Object.assign({ name }, value));
        }
    }
    return builder;
}
exports.toParametersListing = toParametersListing;
/**
 * Convert to legacy automation-client "editor" signature
 * @param {CodeTransformOrTransforms<PARAMS>} ctot
 * @param {ProjectPredicate} projectPredicate
 * @return {ProjectEditor<PARAMS>}
 */
function toScalarProjectEditor(ctot, sdm, projectPredicate) {
    const unguarded = Array.isArray(ctot)
        ? projectEditorOps_1.chainEditors(...ctot.map(c => toProjectEditor(c, sdm)))
        : toProjectEditor(ctot, sdm);
    if (!!projectPredicate) {
        // Filter out this project if it doesn't match the predicate
        return async (p, context, params) => {
            return (await projectPredicate(p))
                ? unguarded(p, context, params)
                : Promise.resolve({ success: true, edited: false, target: p });
        };
    }
    return unguarded;
}
exports.toScalarProjectEditor = toScalarProjectEditor;
// Convert to an old style, automation-client, ProjectEditor to allow
// underlying code to work for now
function toProjectEditor(ct, sdm) {
    return async (p, ctx, params) => {
        const ci = toCommandListenerInvocation(p, ctx, params, toMachineOptions_1.toMachineOptions(sdm));
        ci.credentials = await resolveCredentialsPromise(ci.credentials);
        // Mix in handler context for old style callers
        const n = await ct(p, Object.assign(Object.assign({}, ctx), ci), params);
        if (n === undefined) {
            // The transform returned void
            return { target: p, edited: await isDirty(p), success: true };
        }
        const r = n;
        try {
            return Project_1.isProject(r) ? projectEditor_1.successfulEdit(r, await isDirty(r)) : r;
        }
        catch (e) {
            return projectEditor_1.failedEdit(p, e);
        }
    };
}
async function isDirty(p) {
    if (isGitProject(p)) {
        try {
            const status = await p.gitStatus();
            return !status.isClean;
        }
        catch (_a) {
            // Ignore
        }
    }
    return undefined;
}
function isGitProject(p) {
    const maybe = p;
    return !!maybe.gitStatus;
}
/**
 * Return a parameters maker that is targeting aware
 * @param {Maker<PARAMS>} paramsMaker
 * @param targets targets parameters to set if necessary
 * @return {Maker<EditorOrReviewerParameters & PARAMS>}
 */
function toRepoTargetingParametersMaker(paramsMaker, targets) {
    const sampleParams = constructionUtils_1.toFactory(paramsMaker)();
    return RepoTargetingParameters_1.isRepoTargetingParameters(sampleParams)
        ? paramsMaker
        : () => {
            const rawParms = constructionUtils_1.toFactory(paramsMaker)();
            const allParms = rawParms;
            const targetsInstance = constructionUtils_1.toFactory(targets)();
            allParms.targets = targetsInstance;
            return allParms;
        };
}
exports.toRepoTargetingParametersMaker = toRepoTargetingParametersMaker;
function toEditModeOrFactory(ctr, ci) {
    const description = ctr.description || ctr.name;
    if (!!ctr.transformPresentation) {
        return (p) => ctr.transformPresentation(Object.assign(Object.assign({}, ci), { progressLog: new LoggingProgressLog_1.LoggingProgressLog(ctr.name, "debug") }), p);
    }
    // Get EditMode from parameters if possible
    if (TransformModeSuggestion_1.isTransformModeSuggestion(ci.parameters)) {
        const tms = ci.parameters;
        return new editModes_1.PullRequest(tms.desiredBranchName, tms.desiredPullRequestTitle || description);
    }
    // Default it if not supplied
    return new editModes_1.PullRequest(`transform-${gitBranchCompatible(ctr.name)}-${dateFormat_1.formatDate()}`, description);
}
/**
 * Takes a potential git branch name and returns a legalised iteration of it
 *
 * @param name the git branch name to sanitise.
 */
function gitBranchCompatible(name) {
    // handles spaces and .. ~ : ^ ? * [ @{
    let branchName = name.replace(/\s+|(\.\.)+|~+|:+|\^+|\?+|\*+|\[+|(\@\{)+/g, "_");
    // handles double slashes
    branchName = branchName.replace(/(\/\/)+|(\\)+/g, "/");
    // handles back slashes
    branchName = branchName.replace(/\\+/g, "/");
    if (branchName.startsWith(".") || branchName.startsWith("/")) {
        branchName = branchName.substring(1);
    }
    if (branchName.endsWith(".") || branchName.endsWith("/")) {
        branchName = branchName.slice(0, -1);
    }
    const lock = ".lock";
    if (branchName.endsWith(lock)) {
        branchName = branchName.slice(0, -lock.length);
    }
    if (branchName === "@") {
        branchName = "at";
    }
    // handles ascii control characters
    branchName = branchName.replace(/[\x00-\x1F\x7F]+/g, "");
    return branchName;
}
exports.gitBranchCompatible = gitBranchCompatible;
async function resolveCredentialsPromise(creds) {
    if (creds instanceof Promise) {
        try {
            return await creds;
        }
        catch (e) {
            logger_1.logger.debug(e.message);
        }
    }
    else if (!!creds) {
        return creds;
    }
    return undefined;
}
exports.resolveCredentialsPromise = resolveCredentialsPromise;
//# sourceMappingURL=handlerRegistrations.js.map