All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.dangernoodle.grt.utils.RepositoryMerger Maven / Gradle / Ivy

package io.dangernoodle.grt.utils;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

import io.dangernoodle.grt.Repository;
import io.dangernoodle.grt.Repository.Settings;
import io.dangernoodle.grt.Repository.Settings.AccessRestrictions;
import io.dangernoodle.grt.Repository.Settings.Branches;
import io.dangernoodle.grt.Repository.Settings.Branches.Protection;
import io.dangernoodle.grt.Repository.Settings.Branches.Protection.RequireReviews;
import io.dangernoodle.grt.Repository.Settings.Branches.Protection.RequiredChecks;
import io.dangernoodle.grt.Repository.Settings.Color;
import io.dangernoodle.grt.Repository.Settings.Permission;
import io.dangernoodle.grt.utils.JsonTransformer.JsonObject;


public class RepositoryMerger
{
    private JsonTransformer transformer;

    public RepositoryMerger(JsonTransformer transformer)
    {
        this.transformer = transformer;
    }

    public Repository merge(File overrides, File defaults) throws IOException
    {
        Repository deRepo = new Repository(transformer.validate(defaults));
        Repository ovRepo = new Repository(transformer.validate(overrides));

        return merge(ovRepo, deRepo);
    }

    public Repository merge(Repository repository) throws IllegalStateException
    {
        RepositoryBuilder builder = createBuilder();
        return merge(repository, builder.build(), builder);
    }

    public Repository merge(Repository overrides, Repository defaults) throws IllegalStateException
    {
        return merge(overrides, defaults, createBuilder());
    }

    // visible for testing
    RepositoryBuilder createBuilder()
    {
        return new RepositoryBuilder(transformer);
    }

    private void addBranchProtection(String branch, Protection protection, RepositoryBuilder builder)
    {
        builder.requireSignedCommits(branch, merge(protection.getRequireSignedCommits(), false))
               .enforceForAdminstrators(branch, merge(protection.getIncludeAdministrators(), false));

        if (protection.hasRequireReviews())
        {
            addRequireReviews(branch, protection.getRequireReviews(), builder);
        }

        if (protection.hasRequiredChecks())
        {
            addRequireChecks(branch, protection.getRequiredChecks(), builder);
        }

        if (protection.hasRestrictedPushAccess())
        {
            addRestrictedPushAccess(branch, protection, builder);
        }
    }

    private void addRequireChecks(String branch, RequiredChecks requireChecks, RepositoryBuilder builder)
    {
        builder.requireBranchUpToDate(branch, merge(requireChecks.getRequireUpToDate(), false));
        if (requireChecks.hasContexts())
        {
            requireChecks.getContexts()
                         .forEach(context -> builder.addRequiredContext(branch, context));
        }
    }

    private void addRequireReviews(String branch, RequireReviews requireReviews, RepositoryBuilder builder)
    {
        builder.requireReviews(branch)
               .requiredReviewers(branch, merge(requireReviews.getRequiredReviewers(), 1))
               .dismissStaleApprovals(branch, merge(requireReviews.getDismissStaleApprovals(), false))
               .requireCodeOwnerReview(branch, merge(requireReviews.getRequireCodeOwner(), false));

        if (requireReviews.hasDismissalRestrictions())
        {
            AccessRestrictions restrictions = requireReviews.getDismissalRestrictions();
            if (restrictions.hasTeams())
            {
                restrictions.getTeams()
                            .forEach(team -> builder.addTeamReviewDismisser(branch, team));
            }

            if (restrictions.hasUsers())
            {
                restrictions.getUsers()
                            .forEach(user -> builder.addUserReviewDismisser(branch, user));
            }
        }
    }

    private void addRestrictedPushAccess(String branch, Protection protection, RepositoryBuilder builder)
    {
        builder.restrictPushAccess(branch);

        AccessRestrictions restrictions = protection.getPushAccess();
        if (restrictions.hasTeams())
        {
            restrictions.getTeams()
                        .forEach(team -> builder.addTeamPushAccess(branch, team));
        }

        if (restrictions.hasUsers())
        {
            restrictions.getUsers()
                        .forEach(user -> builder.addUserPushAccess(branch, user));
        }
    }

    private String getPrimaryBranch(Branches overrides, Branches defaults)
    {
        String branch = overrides.getDefault();
        if (branch == null)
        {
            branch = defaults.getDefault();
            if (branch == null)
            {
                branch = "master";
            }
        }

        return branch;
    }

    private boolean merge(Boolean override, Boolean defaults)
    {
        return merge(override, defaults, false);
    }

    private Collection merge(Collection override, Collection defaults)
    {
        return merge(override, defaults, Collections.emptyList());
    }

    private int merge(Integer override, Integer defaults)
    {
        return merge(override, defaults, 1);
    }

    private  void merge(Map overrides, Map defaults, Callback callback)
    {
        Map merged = merge(overrides, defaults, Collections.emptyMap());

        if (merged.isEmpty())
        {
            callback.add();
        }
        else
        {
            merged.forEach(callback::add);
        }
    }

    private Repository merge(Repository overrides, Repository defaults, RepositoryBuilder builder) throws IllegalStateException
    {
        // there can be only one
        builder.setName(overrides.getName())
               .setOrganization(mergeOrganization(overrides, defaults))
               .setDescription(overrides.getDescription())
               .setHomepage(overrides.getHomepage())
               .setIgnoreTemplate(mergeIgnoreTemplate(overrides, defaults))
               .setLicenseTemplate(mergeLicenseTemplate(overrides, defaults));

        mergeSettings(overrides.getSettings(), defaults.getSettings(), builder);

        mergePlugins(overrides, defaults, builder);
        mergeWorkflow(overrides, defaults, builder);

        return builder.build();
    }

    private  T merge(T override, T defaults, T dflt)
    {
        if (override != null)
        {
            return override;
        }

        if (defaults != null)
        {
            return defaults;
        }

        return dflt;
    }

    private void mergeBranches(Branches overrides, Branches defaults, RepositoryBuilder builder)
    {
        String primary = getPrimaryBranch(overrides, defaults);
        builder.setPrimaryBranch(primary);

        Collection branches = new ArrayList<>(merge(overrides.getOther(), defaults.getOther()));
        builder.addOtherBranches(branches);

        branches.add(primary);
        branches.forEach(branch -> mergeProtections(overrides, defaults, branch, builder));
    }

    private String mergeIgnoreTemplate(Repository overrides, Repository defaults)
    {
        return merge(overrides.getIgnoreTemplate(), defaults.getIgnoreTemplate(), null);
    }

    private String mergeLicenseTemplate(Repository overrides, Repository defaults)
    {
        return merge(overrides.getLicenseTemplate(), defaults.getLicenseTemplate(), null);
    }

    private String mergeOrganization(Repository overrides, Repository defaults) throws IllegalStateException
    {
        String organization = overrides.getOrganization();
        if (organization == null)
        {
            organization = defaults.getOrganization();
            if (organization == null)
            {
                throw new IllegalStateException("organization must be specified");
            }
        }

        return organization;
    }

    private Map mergePlugins(Map overrides, Map defaults)
    {
        Map merged = new HashMap<>(defaults);

        // can't use the 'merge' function b/c we need to allow null values
        overrides.forEach((key, value) -> {
            if (!merged.containsKey(key))
            {
                merged.put(key, value);
            }
            else
            {
                merged.put(key, mergePlugins(value, merged.get(key)));
            }
        });

        return merged;
    }

    @SuppressWarnings("unchecked")
    private Object mergePlugins(Object override, Object dflt)
    {
        if (dflt instanceof Map)
        {
            return mergePlugins((Map) override, (Map) dflt);
        }

        return override;
    }

    @SuppressWarnings("unchecked")
    private void mergePlugins(Repository overrides, Repository defaults, RepositoryBuilder builder)
    {
        Map dePlugins = Optional.ofNullable(defaults.getPlugins())
                                                    .orElse(Collections.emptyMap());

        Map ovPlugins = Optional.ofNullable(overrides.getPlugins())
                                                    .orElse(Collections.emptyMap());

        Map merged = new HashMap<>();
        dePlugins.forEach((key, value) -> merged.put(key, value.toMap()));
        ovPlugins.forEach((key, value) -> merged.put(key, mergePlugins(value.toMap(), merged.get(key))));

        merged.forEach(builder::addPlugin);
    }

    private void mergeProtections(Branches overrides, Branches defaults, String branch, RepositoryBuilder builder)
    {
        Protection deProtection = defaults.getProtection(branch);
        Protection ovProtection = overrides.getProtection(branch);

        if (overrides.hasProtection(branch))
        {
            if (ovProtection.isEnabled())
            {
                addBranchProtection(branch, ovProtection, builder);
            }
        }
        else if (defaults.hasProtection(branch))
        {
            if (deProtection.isEnabled())
            {
                addBranchProtection(branch, deProtection, builder);
            }
        }
    }

    private void mergeSettings(Settings overrides, Settings defaults, RepositoryBuilder builder)
    {
        builder.setArchived(merge(overrides.isArchived(), defaults.isArchived()))
               .setDeleteBranchOnMerge(merge(overrides.deleteBranchOnMerge(), defaults.deleteBranchOnMerge()))
               .setInitialize(merge(overrides.autoInitialize(), defaults.autoInitialize()))
               .setIssues(merge(overrides.enableIssues(), defaults.enableIssues(), true))
               .setMergeCommits(merge(overrides.enableMergeCommits(), defaults.enableMergeCommits(), true))
               .setRebaseMerge(merge(overrides.enableRebaseMerge(), defaults.enableRebaseMerge(), true))
               .setSquashMerge(merge(overrides.enableSquashMerge(), defaults.enableSquashMerge(), true))
               .setPrivate(merge(overrides.isPrivate(), defaults.isPrivate()))
               .setWiki(merge(overrides.enableWiki(), defaults.enableWiki(), true));

        merge(overrides.getLabels(), defaults.getLabels(), new Callback()
        {
            @Override
            public void add()
            {
                builder.addLabels();
            }

            @Override
            public void add(String key, Color value)
            {
                builder.addLabel(key, value);
            }
        });

        merge(overrides.getCollaborators(), defaults.getCollaborators(), new Callback()
        {
            @Override
            public void add()
            {
                builder.addCollaborators();
            }

            @Override
            public void add(String key, Permission value)
            {
                builder.addCollaborator(key, value);
            }
        });

        merge(overrides.getTeams(), defaults.getTeams(), new Callback()
        {
            @Override
            public void add()
            {
                builder.addTeams();
            }

            @Override
            public void add(String key, Permission value)
            {
                builder.addTeam(key, value);
            }
        });

        mergeBranches(overrides.getBranches(), defaults.getBranches(), builder);
    }

    private void mergeWorkflow(Repository overrides, Repository defaults, RepositoryBuilder builder)
    {
        merge(overrides.getWorkflow(), defaults.getWorkflow()).forEach(builder::addWorkflow);
    }

    private interface Callback
    {
        void add();

        void add(String key, V value);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy