From af19846800e8aa5cddb4eeae8099b55743bd10c4 Mon Sep 17 00:00:00 2001 From: johnl Date: Mon, 8 Apr 2019 16:26:03 +0100 Subject: [PATCH 01/12] First round of changes to support new Bitbucket api --- .idea/compiler.xml | 13 ++ .idea/vcs.xml | 6 + pom.xml | 12 +- .../plugin/hook/ChangesetServiceImpl.java | 9 +- .../filehooks/plugin/hook/FileNameHook.java | 179 ++---------------- .../filehooks/plugin/hook/FileSizeHook.java | 110 ++++++----- .../stash/filehooks/plugin/hook/GitUtils.java | 5 +- 7 files changed, 117 insertions(+), 217 deletions(-) create mode 100644 .idea/compiler.xml create mode 100644 .idea/vcs.xml diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 0000000..460d80e --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/pom.xml b/pom.xml index 75c7b4f..5cdb28a 100644 --- a/pom.xml +++ b/pom.xml @@ -25,7 +25,7 @@ atlassian-plugin - 5.4.1 + 6.1.0 ${bitbucket.version} 6.3.0 1.2.3 @@ -113,6 +113,16 @@ ${commons-lang.version} provided + + com.google.guava + guava + r05 + + + com.google.code.findbugs + jsr305 + 3.0.2 + diff --git a/src/main/java/org/christiangalsterer/stash/filehooks/plugin/hook/ChangesetServiceImpl.java b/src/main/java/org/christiangalsterer/stash/filehooks/plugin/hook/ChangesetServiceImpl.java index 9881ad4..1cda8cd 100644 --- a/src/main/java/org/christiangalsterer/stash/filehooks/plugin/hook/ChangesetServiceImpl.java +++ b/src/main/java/org/christiangalsterer/stash/filehooks/plugin/hook/ChangesetServiceImpl.java @@ -3,6 +3,7 @@ import com.atlassian.bitbucket.commit.Changeset; import com.atlassian.bitbucket.commit.Commit; import com.atlassian.bitbucket.content.Change; +import com.atlassian.bitbucket.nav.NavBuilder; import com.atlassian.bitbucket.repository.Ref; import com.atlassian.bitbucket.repository.RefChange; import com.atlassian.bitbucket.repository.Repository; @@ -10,8 +11,8 @@ import com.atlassian.bitbucket.scm.CommitsCommandParameters; import com.atlassian.bitbucket.scm.ScmService; import com.atlassian.bitbucket.util.PageRequest; -import com.atlassian.bitbucket.util.PageUtils; -import com.atlassian.bitbucket.util.PagedIterable; +import com.atlassian.bitbucket.util.PageRequest; +//import com.atlassian.bitbucket.util.PagedIter import com.google.common.collect.Iterables; import java.util.*; @@ -20,7 +21,7 @@ public class ChangesetServiceImpl implements ChangesetService { - private static final PageRequest PAGE_REQUEST = PageUtils.newRequest(0, PageRequest.MAX_PAGE_LIMIT); + private static final PageRequest PAGE_REQUEST = PageRequest.MAX_PAGE_LIMIT; private static final int MAX_CHANGES_PER_COMMIT = PageRequest.MAX_PAGE_LIMIT; private final ScmService scmService; @@ -102,7 +103,7 @@ private Iterable getChangesets(final Repository repository, Iterable< .collect(Collectors.toSet()); if (!commitIds.isEmpty()) { - changesets = new PagedIterable<>(pageRequest -> scmService.getCommandFactory(repository).changesets( + changesets = new (pageRequest -> scmService.getCommandFactory(repository).changesets( new ChangesetsCommandParameters.Builder() .commitIds(commitIds) .maxChangesPerCommit(MAX_CHANGES_PER_COMMIT) diff --git a/src/main/java/org/christiangalsterer/stash/filehooks/plugin/hook/FileNameHook.java b/src/main/java/org/christiangalsterer/stash/filehooks/plugin/hook/FileNameHook.java index fd57339..ea73bd5 100644 --- a/src/main/java/org/christiangalsterer/stash/filehooks/plugin/hook/FileNameHook.java +++ b/src/main/java/org/christiangalsterer/stash/filehooks/plugin/hook/FileNameHook.java @@ -8,19 +8,22 @@ import com.atlassian.bitbucket.content.ChangeType; import com.atlassian.bitbucket.content.ChangesRequest; import com.atlassian.bitbucket.hook.HookResponse; -import com.atlassian.bitbucket.hook.repository.PreReceiveRepositoryHook; +import com.atlassian.bitbucket.hook.repository.PreRepositoryHook; +import com.atlassian.bitbucket.hook.repository.PostRepositoryHook; +import com.atlassian.bitbucket.hook.repository.PostRepositoryHookContext; import com.atlassian.bitbucket.hook.repository.RepositoryHookContext; -import com.atlassian.bitbucket.hook.repository.RepositoryMergeRequestCheck; +import com.atlassian.bitbucket.hook.repository.RepositoryHookRequest; +import com.atlassian.bitbucket.hook.repository.RepositoryMergeCheck; import com.atlassian.bitbucket.hook.repository.RepositoryMergeRequestCheckContext; import com.atlassian.bitbucket.i18n.I18nService; import com.atlassian.bitbucket.pull.PullRequest; import com.atlassian.bitbucket.pull.PullRequestRef; import com.atlassian.bitbucket.repository.RefChange; import com.atlassian.bitbucket.repository.Repository; -import com.atlassian.bitbucket.scm.git.GitScmConfig; -import com.atlassian.bitbucket.scm.git.command.GitCommandBuilderFactory; +import com.atlassian.bitbucket.scm.Scm; +import com.atlassian.bitbucket.scm.ScmCommandBuilder; import com.atlassian.bitbucket.scm.pull.MergeRequest; -import com.atlassian.bitbucket.setting.RepositorySettingsValidator; +import com.atlassian.bitbucket.setting.SettingsValidator; import com.atlassian.bitbucket.setting.Settings; import com.atlassian.bitbucket.setting.SettingsValidationErrors; import com.google.common.base.Strings; @@ -43,164 +46,18 @@ /** * Checks the name and path of a file in the pre-receive phase and rejects the push when the changeset contains files which match the configured file name pattern. */ -public class FileNameHook implements PreReceiveRepositoryHook, RepositorySettingsValidator, RepositoryMergeRequestCheck { +public class FileNameHook implements PostRepositoryHook { - private static final String SETTINGS_INCLUDE_PATTERN = "pattern"; - private static final String SETTINGS_EXCLUDE_PATTERN = "pattern-exclude"; - private static final String SETTINGS_BRANCHES_PATTERN = "pattern-branches"; - - private final ChangesetService changesetService; - private final I18nService i18n; - private final CommitService commitService; - private final MergeBaseResolver mergeBaseResolver; - - public FileNameHook(GitCommandBuilderFactory builderFactory, CommitService commitService, ChangesetService changesetService, I18nService i18n, GitScmConfig gitScmConfig) { - this.changesetService = changesetService; - this.i18n = i18n; - this.commitService = commitService; - this.mergeBaseResolver = new MergeBaseResolver(builderFactory, gitScmConfig, commitService); - } - - @Override - public boolean onReceive(@Nonnull RepositoryHookContext context, @Nonnull Collection refChanges, @Nonnull HookResponse hookResponse) { - Repository repository = context.getRepository(); - FileNameHookSetting setting = getSettings(context.getSettings()); - Optional branchesPattern = setting.getBranchesPattern(); - - Collection filteredRefChanges = refChanges.stream().filter(isNotDeleteRefChange).filter(isNotTagRefChange).collect(Collectors.toList()); - - if(branchesPattern.isPresent()) { - filteredRefChanges = filteredRefChanges.stream().filter(matchesBranchPattern(branchesPattern.get())).collect(Collectors.toList()); - } - - Iterable changes = changesetService.getChanges(filteredRefChanges, repository); - - Collection filteredPaths = StreamSupport.stream(changes.spliterator(), false).filter(isNotDeleteChange).map(Functions.CHANGE_TO_PATH).filter(setting.getIncludePattern().asPredicate()).collect(Collectors.toList()); - - if(setting.getExcludePattern().isPresent()) { - Pattern excludePattern = setting.getExcludePattern().get(); - filteredPaths = filteredPaths.stream().filter(excludePattern.asPredicate().negate()).collect(Collectors.toList()); - } - - if (filteredPaths.size() > 0) { - hookResponse.out().println("================================="); - for (String path : filteredPaths) { - String msg; - if(branchesPattern.isPresent()) { - msg = String.format("File [%s] violates file name pattern [%s] for branch [%s].", path, setting.getIncludePattern().pattern(), branchesPattern.get()); - } else { - msg = String.format("File [%s] violates file name pattern [%s].", path, setting.getIncludePattern().pattern()); - } - hookResponse.out().println(msg); - } - hookResponse.out().println("================================="); - return false; - } - return true; - } - - private FileNameHookSetting getSettings(Settings settings) { - String includeRegex = settings.getString(SETTINGS_INCLUDE_PATTERN); - String excludeRegex = settings.getString(SETTINGS_EXCLUDE_PATTERN); - String branchesRegex = settings.getString(SETTINGS_BRANCHES_PATTERN); - - return new FileNameHookSetting(includeRegex, excludeRegex, branchesRegex); - } + private static final Logger log = LoggerFactory.getLogger(LoggingPostRepositoryHook.class); @Override - public void validate(Settings settings, SettingsValidationErrors errors, Repository repository) { - - if (Strings.isNullOrEmpty(settings.getString(SETTINGS_INCLUDE_PATTERN))){ - errors.addFieldError(SETTINGS_INCLUDE_PATTERN, i18n.getText("filename-hook.error.pattern", "Pattern is not a valid regular expression")); - } else { - try { - Pattern.compile(settings.getString(SETTINGS_INCLUDE_PATTERN, "")); - } catch (PatternSyntaxException e) { - errors.addFieldError(SETTINGS_INCLUDE_PATTERN, i18n.getText("filename-hook.error.pattern", "Pattern is not a valid regular expression")); - } - } - - if (!Strings.isNullOrEmpty(settings.getString(SETTINGS_EXCLUDE_PATTERN))){ - try { - Pattern.compile(settings.getString(SETTINGS_EXCLUDE_PATTERN)); - } catch (PatternSyntaxException e) { - errors.addFieldError(SETTINGS_EXCLUDE_PATTERN, i18n.getText("filename-hook.error.pattern", "Pattern is not a valid regular expression")); - } - } - - if (!Strings.isNullOrEmpty(settings.getString(SETTINGS_BRANCHES_PATTERN))) { - try { - Pattern.compile(settings.getString(SETTINGS_BRANCHES_PATTERN)); - } catch (PatternSyntaxException e) { - errors.addFieldError(SETTINGS_BRANCHES_PATTERN, i18n.getText("filename-hook.error.pattern", "Pattern is not a valid regular expression")); - } - } - } - - /** - * Callback, collecting all the paths, changed in the requested change - * range. - */ - private static class ChangedPathsCollector extends AbstractChangeCallback { - private final Collection changedPaths = new HashSet<>(); - - @Override - public boolean onChange(Change change) throws IOException { - if (change.getType() != ChangeType.DELETE) { - changedPaths.add(change.getPath().toString()); - } - return true; - } - - Collection getChangedPaths() { - return changedPaths; - } - - } - - private String getPullRequestError(Collection filteredFiles) { - final StringBuilder sb = new StringBuilder(); - final Iterator iter = filteredFiles.iterator(); - while (iter.hasNext()) { - sb.append(iter.next()); - if (iter.hasNext()) { - sb.append(", "); - } - } - return sb.toString(); - } - - @Override - public void check(RepositoryMergeRequestCheckContext context) { - final MergeRequest request = context.getMergeRequest(); - final PullRequest pr = request.getPullRequest(); - final Commit prFrom = getChangeSet(pr.getFromRef()); - final Commit prTo = getChangeSet(pr.getToRef()); - final Commit base = mergeBaseResolver.findMergeBase(prFrom, prTo); - final FileNameHookSetting setting = getSettings(context.getSettings()); - - final ChangesRequest.Builder builder = new ChangesRequest.Builder(prFrom.getRepository(), prFrom.getId()); - if (base.getId() != null) { - builder.sinceId(base.getId()); - } - final ChangesRequest pathsRequest = builder.build(); - final ChangedPathsCollector pathsCallback = new ChangedPathsCollector(); - commitService.streamChanges(pathsRequest, pathsCallback); - Collection filteredFiles = pathsCallback.getChangedPaths(); - filteredFiles = filteredFiles.stream().filter(setting.getIncludePattern().asPredicate()).collect(Collectors.toList()); - - if(setting.getExcludePattern().isPresent()) { - Pattern excludePattern = setting.getExcludePattern().get(); - filteredFiles = filteredFiles.stream().filter(excludePattern.asPredicate().negate()).collect(Collectors.toList()); - } - - if (filteredFiles.size() > 0) { - request.veto(i18n.getText("filename-hook.mergecheck.veto", "File Name Hook: The following files violate the file name pattern [{0}]:", setting.getIncludePattern().pattern()), getPullRequestError(filteredFiles)); - } - } - - private Commit getChangeSet(PullRequestRef prRef) { - final CommitRequest.Builder builder = new CommitRequest.Builder(prRef.getRepository(), prRef.getLatestCommit()); - return commitService.getCommit(builder.build()); + public void postUpdate(@Nonnull PostRepositoryHookContext context, + @Nonnull RepositoryHookRequest hookRequest) { + log.info("[{}] {} updated [{}]", + hookRequest.getRepository(), + hookRequest.getTrigger().getId(), + hookRequest.getRefChanges().stream() + .map(change -> change.getRef().getId()) + .collect(Collectors.joining(", "))); } } diff --git a/src/main/java/org/christiangalsterer/stash/filehooks/plugin/hook/FileSizeHook.java b/src/main/java/org/christiangalsterer/stash/filehooks/plugin/hook/FileSizeHook.java index dfc3a1a..5ef0889 100644 --- a/src/main/java/org/christiangalsterer/stash/filehooks/plugin/hook/FileSizeHook.java +++ b/src/main/java/org/christiangalsterer/stash/filehooks/plugin/hook/FileSizeHook.java @@ -3,13 +3,13 @@ import com.atlassian.bitbucket.commit.Commit; import com.atlassian.bitbucket.content.Change; import com.atlassian.bitbucket.hook.HookResponse; -import com.atlassian.bitbucket.hook.repository.PreReceiveRepositoryHook; -import com.atlassian.bitbucket.hook.repository.RepositoryHookContext; +import com.atlassian.bitbucket.hook.repository.*; import com.atlassian.bitbucket.repository.RefChange; import com.atlassian.bitbucket.repository.Repository; import com.atlassian.bitbucket.scm.Command; +import com.atlassian.bitbucket.scm.ScmCommandBuilder; +import com.atlassian.bitbucket.scm.ScmCommandFactory; import com.atlassian.bitbucket.scm.PluginCommandBuilderFactory; -import com.atlassian.bitbucket.scm.git.command.GitCommandBuilderFactory; import com.atlassian.bitbucket.setting.Settings; import javax.annotation.Nonnull; @@ -25,25 +25,68 @@ /** * Checks the size of a file in the pre-receive phase and rejects the push when the changeset contains files which exceed the configured file size limit. */ -public class FileSizeHook implements PreReceiveRepositoryHook { +public class FileSizeHook implements PreRepositoryHook { private static final int MAX_SETTINGS = 5; private static final String SETTINGS_INCLUDE_PATTERN_PREFIX = "pattern-"; private static final String SETTINGS_EXCLUDE_PATTERN_PREFIX = "pattern-exclude-"; private static final String SETTINGS_SIZE_PREFIX = "size-"; private static final String SETTINGS_BRANCHES_PATTERN_PREFIX = "pattern-branches-"; - private final ChangesetService changesetService; - private final PluginCommandBuilderFactory commandFactory; + private final commandFactory ScmCommandFactory; + - public FileSizeHook(ChangesetService changesetService, GitCommandBuilderFactory commandFactory) { + public FileSizeHook(ChangesetService changesetService) { this.changesetService = changesetService; - this.commandFactory = commandFactory; } + + private List getSettings(Settings settings) { + List configurations = new ArrayList<>(); + String includeRegex; + Long size; + String excludeRegex; + String branchesRegex; + + for (int i = 1; i <= MAX_SETTINGS; i++) { + includeRegex = settings.getString(SETTINGS_INCLUDE_PATTERN_PREFIX + i); + if (includeRegex != null) { + excludeRegex = settings.getString(SETTINGS_EXCLUDE_PATTERN_PREFIX + i); + size = settings.getLong(SETTINGS_SIZE_PREFIX + i); + branchesRegex = settings.getString(SETTINGS_BRANCHES_PATTERN_PREFIX + i); + configurations.add(new FileSizeHookSetting(size, includeRegex, excludeRegex, branchesRegex)); + } + } + + return configurations; + } + + + private Map getSizeForContentIds(final Repository repository, Iterable contentIds) { + CatFileBatchCheckHandler handler = new CatFileBatchCheckHandler(contentIds); + repository.getScmId(). + Command> cmd = commandFactory.builder(repository) + .command("cat-file") + .argument("--batch-check") + .inputHandler(handler) + .build(handler); + return filterOutNullSizes(cmd.call()); + } + + + private Map filterOutNullSizes(Map sizes) { + return sizes.entrySet() + .stream() + .filter(entry -> entry.getValue() != null) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + } + + + @Nonnull @Override - public boolean onReceive(@Nonnull RepositoryHookContext context, @Nonnull Collection refChanges, @Nonnull HookResponse hookResponse) { - Repository repository = context.getRepository(); + public RepositoryHookResult preUpdate(@Nonnull PreRepositoryHookContext context, + @Nonnull RepositoryHookRequest request) { + Repository repository = request.getRepository(); List settings = getSettings(context.getSettings()); FlatteningCachingResolver changesByCommit = new FlatteningCachingResolver<>(); @@ -57,7 +100,7 @@ public boolean onReceive(@Nonnull RepositoryHookContext context, @Nonnull Collec Long maxFileSize = setting.getSize(); Optional branchesPattern = setting.getBranchesPattern(); - Stream filteredRefChanges = refChanges.stream(); + Stream filteredRefChanges = request.getRefChanges().stream(); if (branchesPattern.isPresent()) { filteredRefChanges = filteredRefChanges @@ -96,13 +139,15 @@ public boolean onReceive(@Nonnull RepositoryHookContext context, @Nonnull Collec pathAndSizes.put(maxFileSize, violatingPaths); } + RepositoryHookResult result; + boolean hookPassed = true; for (Long maxFileSize : pathAndSizes.keySet()) { Collection paths = pathAndSizes.get(maxFileSize); if (paths.size() > 0) { hookPassed = false; - hookResponse.out().println("=== File Size Hook ==="); + RepositoryHookResult.Builder.class.out().println("=== File Size Hook ==="); hookResponse.out().println(""); for (String path : paths) { hookResponse.out().println(String.format("File [%s] is too large. Maximum allowed file size is %s bytes.", path, maxFileSize)); @@ -113,43 +158,10 @@ public boolean onReceive(@Nonnull RepositoryHookContext context, @Nonnull Collec } } - return hookPassed; - } - - private List getSettings(Settings settings) { - List configurations = new ArrayList<>(); - String includeRegex; - Long size; - String excludeRegex; - String branchesRegex; - for (int i = 1; i <= MAX_SETTINGS; i++) { - includeRegex = settings.getString(SETTINGS_INCLUDE_PATTERN_PREFIX + i); - if (includeRegex != null) { - excludeRegex = settings.getString(SETTINGS_EXCLUDE_PATTERN_PREFIX + i); - size = settings.getLong(SETTINGS_SIZE_PREFIX + i); - branchesRegex = settings.getString(SETTINGS_BRANCHES_PATTERN_PREFIX + i); - configurations.add(new FileSizeHookSetting(size, includeRegex, excludeRegex, branchesRegex)); - } + if (hookPassed) { + return RepositoryHookResult.accepted(); + } else { + return new RepositoryHookResult.Builder().veto("File [%s] is too large. Maximum allowed file size is %s bytes.", path, maxFileSize).build(); } - - return configurations; - } - - private Map getSizeForContentIds(final Repository repository, Iterable contentIds) { - CatFileBatchCheckHandler handler = new CatFileBatchCheckHandler(contentIds); - Command> cmd = commandFactory.builder(repository) - .command("cat-file") - .argument("--batch-check") - .inputHandler(handler) - .build(handler); - return filterOutNullSizes(cmd.call()); - } - - private Map filterOutNullSizes(Map sizes) { - return sizes.entrySet() - .stream() - .filter(entry -> entry.getValue() != null) - .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); - } } diff --git a/src/main/java/org/christiangalsterer/stash/filehooks/plugin/hook/GitUtils.java b/src/main/java/org/christiangalsterer/stash/filehooks/plugin/hook/GitUtils.java index bfede65..acfff9a 100644 --- a/src/main/java/org/christiangalsterer/stash/filehooks/plugin/hook/GitUtils.java +++ b/src/main/java/org/christiangalsterer/stash/filehooks/plugin/hook/GitUtils.java @@ -2,8 +2,8 @@ import com.atlassian.bitbucket.repository.Repository; import com.atlassian.bitbucket.scm.CommandBuilderSupport; -import com.atlassian.bitbucket.scm.git.GitScmConfig; +/* class GitUtils { private GitUtils() { @@ -14,4 +14,5 @@ static void setAlternateIfCrossRepository(CommandBuilderSupport builder, Repo builder.withEnvironment("GIT_ALTERNATE_OBJECT_DIRECTORIES", config.getObjectsDir(secondRepository).getAbsolutePath()); } } -} \ No newline at end of file +} +*/ \ No newline at end of file From b09d621e0a6b072ed37d3186d8b55cf77c17c2d2 Mon Sep 17 00:00:00 2001 From: johnl Date: Wed, 10 Apr 2019 08:39:44 +0100 Subject: [PATCH 02/12] Add .gitignore file --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ee44a96 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.idea +target From 74bc57ad8634944c600d5686e1b1c8cc114277ef Mon Sep 17 00:00:00 2001 From: johnl Date: Wed, 10 Apr 2019 16:13:25 +0100 Subject: [PATCH 03/12] Updated dependencies and code changes, updated pom.xmls, commented FileName hook for the moment --- pom.xml | 15 ++++++++-- .../plugin/hook/CatFileBatchCheckHandler.java | 2 +- .../plugin/hook/ChangesetServiceImpl.java | 9 +++--- .../filehooks/plugin/hook/FileNameHook.java | 9 ++---- .../filehooks/plugin/hook/FileSizeHook.java | 30 ++++++++----------- .../plugin/hook/FileSizeHookValidator.java | 7 +++-- .../plugin/hook/MergeBaseResolver.java | 9 ++++-- 7 files changed, 43 insertions(+), 38 deletions(-) diff --git a/pom.xml b/pom.xml index 5cdb28a..fed366a 100644 --- a/pom.xml +++ b/pom.xml @@ -25,7 +25,7 @@ atlassian-plugin - 6.1.0 + 6.2.0 ${bitbucket.version} 6.3.0 1.2.3 @@ -61,7 +61,13 @@ bitbucket-spi ${bitbucket.version} provided - + + + com.atlassian.bitbucket.server + bitbucket-scm-common + ${bitbucket.version} + provided + @@ -113,6 +119,11 @@ ${commons-lang.version} provided + + com.atlassian.utils + atlassian-processutils + 1.8.3 + com.google.guava guava diff --git a/src/main/java/org/christiangalsterer/stash/filehooks/plugin/hook/CatFileBatchCheckHandler.java b/src/main/java/org/christiangalsterer/stash/filehooks/plugin/hook/CatFileBatchCheckHandler.java index 07e3644..7935517 100644 --- a/src/main/java/org/christiangalsterer/stash/filehooks/plugin/hook/CatFileBatchCheckHandler.java +++ b/src/main/java/org/christiangalsterer/stash/filehooks/plugin/hook/CatFileBatchCheckHandler.java @@ -40,7 +40,7 @@ public void complete() { @Override protected void processReader(LineReader reader) throws IOException { String line; - while ((line = resetWatchdogAndReadLine(reader)) != null) { + while ((line = reader.readLine()) != null) { String[] split = line.split(" "); // Only process blobs (ie files), ignore folders if (split.length == 3 && split[1].equals("blob")) { diff --git a/src/main/java/org/christiangalsterer/stash/filehooks/plugin/hook/ChangesetServiceImpl.java b/src/main/java/org/christiangalsterer/stash/filehooks/plugin/hook/ChangesetServiceImpl.java index 1cda8cd..8a73e68 100644 --- a/src/main/java/org/christiangalsterer/stash/filehooks/plugin/hook/ChangesetServiceImpl.java +++ b/src/main/java/org/christiangalsterer/stash/filehooks/plugin/hook/ChangesetServiceImpl.java @@ -11,8 +11,8 @@ import com.atlassian.bitbucket.scm.CommitsCommandParameters; import com.atlassian.bitbucket.scm.ScmService; import com.atlassian.bitbucket.util.PageRequest; -import com.atlassian.bitbucket.util.PageRequest; -//import com.atlassian.bitbucket.util.PagedIter +import com.atlassian.bitbucket.util.PageUtils; +import com.atlassian.bitbucket.util.PagedIterable; import com.google.common.collect.Iterables; import java.util.*; @@ -21,7 +21,7 @@ public class ChangesetServiceImpl implements ChangesetService { - private static final PageRequest PAGE_REQUEST = PageRequest.MAX_PAGE_LIMIT; + private static final PageRequest PAGE_REQUEST = PageUtils.newRequest(0, PageRequest.MAX_PAGE_LIMIT); private static final int MAX_CHANGES_PER_COMMIT = PageRequest.MAX_PAGE_LIMIT; private final ScmService scmService; @@ -102,8 +102,9 @@ private Iterable getChangesets(final Repository repository, Iterable< .map(Commit::getId) .collect(Collectors.toSet()); + scmService. if (!commitIds.isEmpty()) { - changesets = new (pageRequest -> scmService.getCommandFactory(repository).changesets( + changesets = new PagedIterable<>(pageRequest -> scmService.getCommandFactory(repository).changesets( new ChangesetsCommandParameters.Builder() .commitIds(commitIds) .maxChangesPerCommit(MAX_CHANGES_PER_COMMIT) diff --git a/src/main/java/org/christiangalsterer/stash/filehooks/plugin/hook/FileNameHook.java b/src/main/java/org/christiangalsterer/stash/filehooks/plugin/hook/FileNameHook.java index ea73bd5..367c287 100644 --- a/src/main/java/org/christiangalsterer/stash/filehooks/plugin/hook/FileNameHook.java +++ b/src/main/java/org/christiangalsterer/stash/filehooks/plugin/hook/FileNameHook.java @@ -48,16 +48,11 @@ */ public class FileNameHook implements PostRepositoryHook { - private static final Logger log = LoggerFactory.getLogger(LoggingPostRepositoryHook.class); + //private static final Logger log = LoggerFactory.getLogger(LoggingPostRepositoryHook.class); @Override public void postUpdate(@Nonnull PostRepositoryHookContext context, @Nonnull RepositoryHookRequest hookRequest) { - log.info("[{}] {} updated [{}]", - hookRequest.getRepository(), - hookRequest.getTrigger().getId(), - hookRequest.getRefChanges().stream() - .map(change -> change.getRef().getId()) - .collect(Collectors.joining(", "))); + } } diff --git a/src/main/java/org/christiangalsterer/stash/filehooks/plugin/hook/FileSizeHook.java b/src/main/java/org/christiangalsterer/stash/filehooks/plugin/hook/FileSizeHook.java index 5ef0889..e15943e 100644 --- a/src/main/java/org/christiangalsterer/stash/filehooks/plugin/hook/FileSizeHook.java +++ b/src/main/java/org/christiangalsterer/stash/filehooks/plugin/hook/FileSizeHook.java @@ -2,16 +2,13 @@ import com.atlassian.bitbucket.commit.Commit; import com.atlassian.bitbucket.content.Change; -import com.atlassian.bitbucket.hook.HookResponse; import com.atlassian.bitbucket.hook.repository.*; import com.atlassian.bitbucket.repository.RefChange; import com.atlassian.bitbucket.repository.Repository; import com.atlassian.bitbucket.scm.Command; import com.atlassian.bitbucket.scm.ScmCommandBuilder; -import com.atlassian.bitbucket.scm.ScmCommandFactory; -import com.atlassian.bitbucket.scm.PluginCommandBuilderFactory; +import com.atlassian.bitbucket.scm.ScmService; import com.atlassian.bitbucket.setting.Settings; - import javax.annotation.Nonnull; import java.util.*; import java.util.regex.Pattern; @@ -33,11 +30,12 @@ public class FileSizeHook implements PreRepositoryHook { private static final String SETTINGS_SIZE_PREFIX = "size-"; private static final String SETTINGS_BRANCHES_PATTERN_PREFIX = "pattern-branches-"; private final ChangesetService changesetService; - private final commandFactory ScmCommandFactory; + private final ScmService scmService; - public FileSizeHook(ChangesetService changesetService) { + public FileSizeHook(ChangesetService changesetService, ScmService scmService) { this.changesetService = changesetService; + this.scmService = scmService; } @@ -63,9 +61,9 @@ private List getSettings(Settings settings) { private Map getSizeForContentIds(final Repository repository, Iterable contentIds) { + ScmCommandBuilder scmCommandBuilder = scmService.createBuilder(repository); CatFileBatchCheckHandler handler = new CatFileBatchCheckHandler(contentIds); - repository.getScmId(). - Command> cmd = commandFactory.builder(repository) + Command> cmd = scmCommandBuilder .command("cat-file") .argument("--batch-check") .inputHandler(handler) @@ -140,21 +138,15 @@ public RepositoryHookResult preUpdate(@Nonnull PreRepositoryHookContext context, } RepositoryHookResult result; - + ArrayList resultList = new ArrayList<>(); boolean hookPassed = true; for (Long maxFileSize : pathAndSizes.keySet()) { Collection paths = pathAndSizes.get(maxFileSize); if (paths.size() > 0) { hookPassed = false; - RepositoryHookResult.Builder.class.out().println("=== File Size Hook ==="); - hookResponse.out().println(""); - for (String path : paths) { - hookResponse.out().println(String.format("File [%s] is too large. Maximum allowed file size is %s bytes.", path, maxFileSize)); - } - hookResponse.out().println(""); - hookResponse.out().println("You may to consider to use Git Large File Storage in Bitbucket, see https://confluence.atlassian.com/bitbucket/git-large-file-storage-in-bitbucket-829078514.html"); - hookResponse.out().println("======================"); + for (String path : paths) + resultList.add(String.format("File [%s] is too large. Maximum allowed file size is %s bytes.", path, maxFileSize)); } } @@ -162,6 +154,8 @@ public RepositoryHookResult preUpdate(@Nonnull PreRepositoryHookContext context, if (hookPassed) { return RepositoryHookResult.accepted(); } else { - return new RepositoryHookResult.Builder().veto("File [%s] is too large. Maximum allowed file size is %s bytes.", path, maxFileSize).build(); + return RepositoryHookResult.rejected("files are too large", Arrays.toString(resultList.toArray())); } + } + } diff --git a/src/main/java/org/christiangalsterer/stash/filehooks/plugin/hook/FileSizeHookValidator.java b/src/main/java/org/christiangalsterer/stash/filehooks/plugin/hook/FileSizeHookValidator.java index c934552..8a8a9ad 100644 --- a/src/main/java/org/christiangalsterer/stash/filehooks/plugin/hook/FileSizeHookValidator.java +++ b/src/main/java/org/christiangalsterer/stash/filehooks/plugin/hook/FileSizeHookValidator.java @@ -2,7 +2,8 @@ import com.atlassian.bitbucket.i18n.I18nService; import com.atlassian.bitbucket.repository.Repository; -import com.atlassian.bitbucket.setting.RepositorySettingsValidator; +import com.atlassian.bitbucket.scope.Scope; +import com.atlassian.bitbucket.setting.SettingsValidator; import com.atlassian.bitbucket.setting.Settings; import com.atlassian.bitbucket.setting.SettingsValidationErrors; import com.google.common.base.Strings; @@ -12,7 +13,7 @@ import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; -public class FileSizeHookValidator implements RepositorySettingsValidator { +public class FileSizeHookValidator implements SettingsValidator { private static final int MAX_SETTINGS = 5; private static final String SETTINGS_INCLUDE_PATTERN_PREFIX = "pattern-"; @@ -27,7 +28,7 @@ public FileSizeHookValidator(I18nService i18n) { } @Override - public void validate(@Nonnull Settings settings, @Nonnull SettingsValidationErrors errors, @Nonnull Repository repository) { + public void validate(@Nonnull Settings settings, @Nonnull SettingsValidationErrors errors, @Nonnull Scope scope) { int patternParams = 0; diff --git a/src/main/java/org/christiangalsterer/stash/filehooks/plugin/hook/MergeBaseResolver.java b/src/main/java/org/christiangalsterer/stash/filehooks/plugin/hook/MergeBaseResolver.java index 22c68f9..944827a 100644 --- a/src/main/java/org/christiangalsterer/stash/filehooks/plugin/hook/MergeBaseResolver.java +++ b/src/main/java/org/christiangalsterer/stash/filehooks/plugin/hook/MergeBaseResolver.java @@ -3,15 +3,17 @@ import com.atlassian.bitbucket.commit.Commit; import com.atlassian.bitbucket.commit.CommitRequest; import com.atlassian.bitbucket.commit.CommitService; -import com.atlassian.bitbucket.scm.git.GitScmConfig; -import com.atlassian.bitbucket.scm.git.command.GitCommandBuilderFactory; -import com.atlassian.bitbucket.scm.git.command.merge.GitMergeBaseBuilder; +//import com.atlassian.bitbucket.scm.git.GitScmConfig; +//import com.atlassian.bitbucket.scm.git.command.GitCommandBuilderFactory; +//import com.atlassian.bitbucket.scm.git.command.merge.GitMergeBaseBuilder; /** * Determines the merge base of a pair of commits. */ class MergeBaseResolver { + /* + private final GitCommandBuilderFactory builderFactory; private final GitScmConfig gitScmConfig; private final CommitService commitService; @@ -37,4 +39,5 @@ Commit findMergeBase(Commit a, Commit b) { return commitService.getCommit(commitRequest); } + */ } \ No newline at end of file From 38ce10be149267dc55343e72a3e3e271f289497a Mon Sep 17 00:00:00 2001 From: johnl Date: Thu, 11 Apr 2019 10:21:34 +0100 Subject: [PATCH 04/12] Final changes --- pom.xml | 29 +++++++++++++++++-- .../plugin/hook/ChangesetServiceImpl.java | 2 -- .../plugin/hook/FileNameHookTest.java | 7 +++-- 3 files changed, 30 insertions(+), 8 deletions(-) diff --git a/pom.xml b/pom.xml index fed366a..6d77895 100644 --- a/pom.xml +++ b/pom.xml @@ -62,12 +62,30 @@ ${bitbucket.version} provided - + + com.atlassian.bitbucket.server + bitbucket-scm-common + ${bitbucket.version} + provided + + com.atlassian.bitbucket.server - bitbucket-scm-common + bitbucket-util ${bitbucket.version} provided - + + + com.atlassian.bitbucket.server + bitbucket-page-objects + ${bitbucket.version} + provided + + + com.atlassian.sal + sal-api + 2.10.0 + provided + @@ -77,6 +95,11 @@ sal-api provided + + com.atlassian.bitbucket.server + bitbucket-util + provided + com.atlassian.bitbucket.server bitbucket-api diff --git a/src/main/java/org/christiangalsterer/stash/filehooks/plugin/hook/ChangesetServiceImpl.java b/src/main/java/org/christiangalsterer/stash/filehooks/plugin/hook/ChangesetServiceImpl.java index 8a73e68..9881ad4 100644 --- a/src/main/java/org/christiangalsterer/stash/filehooks/plugin/hook/ChangesetServiceImpl.java +++ b/src/main/java/org/christiangalsterer/stash/filehooks/plugin/hook/ChangesetServiceImpl.java @@ -3,7 +3,6 @@ import com.atlassian.bitbucket.commit.Changeset; import com.atlassian.bitbucket.commit.Commit; import com.atlassian.bitbucket.content.Change; -import com.atlassian.bitbucket.nav.NavBuilder; import com.atlassian.bitbucket.repository.Ref; import com.atlassian.bitbucket.repository.RefChange; import com.atlassian.bitbucket.repository.Repository; @@ -102,7 +101,6 @@ private Iterable getChangesets(final Repository repository, Iterable< .map(Commit::getId) .collect(Collectors.toSet()); - scmService. if (!commitIds.isEmpty()) { changesets = new PagedIterable<>(pageRequest -> scmService.getCommandFactory(repository).changesets( new ChangesetsCommandParameters.Builder() diff --git a/src/test/java/org/christiangalsterer/stash/filehooks/plugin/hook/FileNameHookTest.java b/src/test/java/org/christiangalsterer/stash/filehooks/plugin/hook/FileNameHookTest.java index 7c0a037..2302cec 100644 --- a/src/test/java/org/christiangalsterer/stash/filehooks/plugin/hook/FileNameHookTest.java +++ b/src/test/java/org/christiangalsterer/stash/filehooks/plugin/hook/FileNameHookTest.java @@ -3,10 +3,9 @@ import com.atlassian.bitbucket.commit.CommitService; import com.atlassian.bitbucket.i18n.I18nService; import com.atlassian.bitbucket.repository.Repository; -import com.atlassian.bitbucket.scm.git.GitScmConfig; -import com.atlassian.bitbucket.scm.git.command.GitCommandBuilderFactory; import com.atlassian.bitbucket.setting.Settings; +/* import org.junit.Before; import org.junit.Test; import org.mockito.Mock; @@ -60,4 +59,6 @@ public void testValidate() throws Exception { private boolean push() { return true; } -} \ No newline at end of file +} + +*/ \ No newline at end of file From 8ae3daee0aa5db770902d7480c6aaa304129b89e Mon Sep 17 00:00:00 2001 From: johnl Date: Thu, 11 Apr 2019 11:03:22 +0100 Subject: [PATCH 05/12] Comment FileName plugin stuff for the moment, haven't had time to update that --- pom.xml | 10 +- .../filehooks/plugin/hook/FileNameHook.java | 168 ++++++++++++++++-- .../plugin/hook/MergeBaseResolver.java | 14 +- src/main/resources/atlassian-plugin.xml | 4 +- 4 files changed, 171 insertions(+), 25 deletions(-) diff --git a/pom.xml b/pom.xml index 6d77895..b8b0468 100644 --- a/pom.xml +++ b/pom.xml @@ -1,9 +1,9 @@ 4.0.0 - org.christiangalsterer - stash-filehooks-plugin - 3.3.2 + com.openet + bitbucket-filehooks-plugin + 4.0 Christian Galsterer https://github.com/christiangalsterer/stash-filehooks-plugin @@ -13,6 +13,10 @@ Krzysztof Malinowski Motorola Solutions, Inc. + + John Lawlor + Openet Telecom Ltd. + diff --git a/src/main/java/org/christiangalsterer/stash/filehooks/plugin/hook/FileNameHook.java b/src/main/java/org/christiangalsterer/stash/filehooks/plugin/hook/FileNameHook.java index 367c287..08bec81 100644 --- a/src/main/java/org/christiangalsterer/stash/filehooks/plugin/hook/FileNameHook.java +++ b/src/main/java/org/christiangalsterer/stash/filehooks/plugin/hook/FileNameHook.java @@ -8,26 +8,17 @@ import com.atlassian.bitbucket.content.ChangeType; import com.atlassian.bitbucket.content.ChangesRequest; import com.atlassian.bitbucket.hook.HookResponse; -import com.atlassian.bitbucket.hook.repository.PreRepositoryHook; -import com.atlassian.bitbucket.hook.repository.PostRepositoryHook; -import com.atlassian.bitbucket.hook.repository.PostRepositoryHookContext; import com.atlassian.bitbucket.hook.repository.RepositoryHookContext; -import com.atlassian.bitbucket.hook.repository.RepositoryHookRequest; -import com.atlassian.bitbucket.hook.repository.RepositoryMergeCheck; import com.atlassian.bitbucket.hook.repository.RepositoryMergeRequestCheckContext; import com.atlassian.bitbucket.i18n.I18nService; import com.atlassian.bitbucket.pull.PullRequest; import com.atlassian.bitbucket.pull.PullRequestRef; import com.atlassian.bitbucket.repository.RefChange; import com.atlassian.bitbucket.repository.Repository; -import com.atlassian.bitbucket.scm.Scm; -import com.atlassian.bitbucket.scm.ScmCommandBuilder; import com.atlassian.bitbucket.scm.pull.MergeRequest; -import com.atlassian.bitbucket.setting.SettingsValidator; import com.atlassian.bitbucket.setting.Settings; import com.atlassian.bitbucket.setting.SettingsValidationErrors; import com.google.common.base.Strings; -import com.google.common.collect.*; import javax.annotation.Nonnull; @@ -46,13 +37,164 @@ /** * Checks the name and path of a file in the pre-receive phase and rejects the push when the changeset contains files which match the configured file name pattern. */ -public class FileNameHook implements PostRepositoryHook { - //private static final Logger log = LoggerFactory.getLogger(LoggingPostRepositoryHook.class); +/** +public class FileNameHook implements PreReceiveRepositoryHook, RepositorySettingsValidator, RepositoryMergeRequestCheck { + + private static final String SETTINGS_INCLUDE_PATTERN = "pattern"; + private static final String SETTINGS_EXCLUDE_PATTERN = "pattern-exclude"; + private static final String SETTINGS_BRANCHES_PATTERN = "pattern-branches"; + + private final ChangesetService changesetService; + private final I18nService i18n; + private final CommitService commitService; + private final MergeBaseResolver mergeBaseResolver; + + public FileNameHook(GitCommandBuilderFactory builderFactory, CommitService commitService, ChangesetService changesetService, I18nService i18n, GitScmConfig gitScmConfig) { + this.changesetService = changesetService; + this.i18n = i18n; + this.commitService = commitService; + this.mergeBaseResolver = new MergeBaseResolver(builderFactory, gitScmConfig, commitService); + } @Override - public void postUpdate(@Nonnull PostRepositoryHookContext context, - @Nonnull RepositoryHookRequest hookRequest) { + public boolean onReceive(@Nonnull RepositoryHookContext context, @Nonnull Collection refChanges, @Nonnull HookResponse hookResponse) { + Repository repository = context.getRepository(); + FileNameHookSetting setting = getSettings(context.getSettings()); + Optional branchesPattern = setting.getBranchesPattern(); + + Collection filteredRefChanges = refChanges.stream().filter(isNotDeleteRefChange).filter(isNotTagRefChange).collect(Collectors.toList()); + + if(branchesPattern.isPresent()) { + filteredRefChanges = filteredRefChanges.stream().filter(matchesBranchPattern(branchesPattern.get())).collect(Collectors.toList()); + } + + Iterable changes = changesetService.getChanges(filteredRefChanges, repository); + + Collection filteredPaths = StreamSupport.stream(changes.spliterator(), false).filter(isNotDeleteChange).map(Functions.CHANGE_TO_PATH).filter(setting.getIncludePattern().asPredicate()).collect(Collectors.toList()); + if(setting.getExcludePattern().isPresent()) { + Pattern excludePattern = setting.getExcludePattern().get(); + filteredPaths = filteredPaths.stream().filter(excludePattern.asPredicate().negate()).collect(Collectors.toList()); + } + + if (filteredPaths.size() > 0) { + hookResponse.out().println("================================="); + for (String path : filteredPaths) { + String msg; + if(branchesPattern.isPresent()) { + msg = String.format("File [%s] violates file name pattern [%s] for branch [%s].", path, setting.getIncludePattern().pattern(), branchesPattern.get()); + } else { + msg = String.format("File [%s] violates file name pattern [%s].", path, setting.getIncludePattern().pattern()); + } + hookResponse.out().println(msg); + } + hookResponse.out().println("================================="); + return false; + } + return true; + } + + private FileNameHookSetting getSettings(Settings settings) { + String includeRegex = settings.getString(SETTINGS_INCLUDE_PATTERN); + String excludeRegex = settings.getString(SETTINGS_EXCLUDE_PATTERN); + String branchesRegex = settings.getString(SETTINGS_BRANCHES_PATTERN); + + return new FileNameHookSetting(includeRegex, excludeRegex, branchesRegex); + } + + @Override + public void validate(Settings settings, SettingsValidationErrors errors, Repository repository) { + + if (Strings.isNullOrEmpty(settings.getString(SETTINGS_INCLUDE_PATTERN))){ + errors.addFieldError(SETTINGS_INCLUDE_PATTERN, i18n.getText("filename-hook.error.pattern", "Pattern is not a valid regular expression")); + } else { + try { + Pattern.compile(settings.getString(SETTINGS_INCLUDE_PATTERN, "")); + } catch (PatternSyntaxException e) { + errors.addFieldError(SETTINGS_INCLUDE_PATTERN, i18n.getText("filename-hook.error.pattern", "Pattern is not a valid regular expression")); + } + } + + if (!Strings.isNullOrEmpty(settings.getString(SETTINGS_EXCLUDE_PATTERN))){ + try { + Pattern.compile(settings.getString(SETTINGS_EXCLUDE_PATTERN)); + } catch (PatternSyntaxException e) { + errors.addFieldError(SETTINGS_EXCLUDE_PATTERN, i18n.getText("filename-hook.error.pattern", "Pattern is not a valid regular expression")); + } + } + + if (!Strings.isNullOrEmpty(settings.getString(SETTINGS_BRANCHES_PATTERN))) { + try { + Pattern.compile(settings.getString(SETTINGS_BRANCHES_PATTERN)); + } catch (PatternSyntaxException e) { + errors.addFieldError(SETTINGS_BRANCHES_PATTERN, i18n.getText("filename-hook.error.pattern", "Pattern is not a valid regular expression")); + } + } + } + + private static class ChangedPathsCollector extends AbstractChangeCallback { + private final Collection changedPaths = new HashSet<>(); + + @Override + public boolean onChange(Change change) throws IOException { + if (change.getType() != ChangeType.DELETE) { + changedPaths.add(change.getPath().toString()); + } + return true; + } + + Collection getChangedPaths() { + return changedPaths; + } + + } + + private String getPullRequestError(Collection filteredFiles) { + final StringBuilder sb = new StringBuilder(); + final Iterator iter = filteredFiles.iterator(); + while (iter.hasNext()) { + sb.append(iter.next()); + if (iter.hasNext()) { + sb.append(", "); + } + } + return sb.toString(); + } + + @Override + public void check(RepositoryMergeRequestCheckContext context) { + final MergeRequest request = context.getMergeRequest(); + final PullRequest pr = request.getPullRequest(); + final Commit prFrom = getChangeSet(pr.getFromRef()); + final Commit prTo = getChangeSet(pr.getToRef()); + final Commit base = mergeBaseResolver.findMergeBase(prFrom, prTo); + final FileNameHookSetting setting = getSettings(context.getSettings()); + + final ChangesRequest.Builder builder = new ChangesRequest.Builder(prFrom.getRepository(), prFrom.getId()); + if (base.getId() != null) { + builder.sinceId(base.getId()); + } + final ChangesRequest pathsRequest = builder.build(); + final ChangedPathsCollector pathsCallback = new ChangedPathsCollector(); + commitService.streamChanges(pathsRequest, pathsCallback); + Collection filteredFiles = pathsCallback.getChangedPaths(); + filteredFiles = filteredFiles.stream().filter(setting.getIncludePattern().asPredicate()).collect(Collectors.toList()); + + if(setting.getExcludePattern().isPresent()) { + Pattern excludePattern = setting.getExcludePattern().get(); + filteredFiles = filteredFiles.stream().filter(excludePattern.asPredicate().negate()).collect(Collectors.toList()); + } + + if (filteredFiles.size() > 0) { + request.veto(i18n.getText("filename-hook.mergecheck.veto", "File Name Hook: The following files violate the file name pattern [{0}]:", setting.getIncludePattern().pattern()), getPullRequestError(filteredFiles)); + } + } + + private Commit getChangeSet(PullRequestRef prRef) { + final CommitRequest.Builder builder = new CommitRequest.Builder(prRef.getRepository(), prRef.getLatestCommit()); + return commitService.getCommit(builder.build()); } } + +*/ \ No newline at end of file diff --git a/src/main/java/org/christiangalsterer/stash/filehooks/plugin/hook/MergeBaseResolver.java b/src/main/java/org/christiangalsterer/stash/filehooks/plugin/hook/MergeBaseResolver.java index 944827a..a71509e 100644 --- a/src/main/java/org/christiangalsterer/stash/filehooks/plugin/hook/MergeBaseResolver.java +++ b/src/main/java/org/christiangalsterer/stash/filehooks/plugin/hook/MergeBaseResolver.java @@ -1,18 +1,17 @@ package org.christiangalsterer.stash.filehooks.plugin.hook; + import com.atlassian.bitbucket.commit.Commit; import com.atlassian.bitbucket.commit.CommitRequest; import com.atlassian.bitbucket.commit.CommitService; -//import com.atlassian.bitbucket.scm.git.GitScmConfig; -//import com.atlassian.bitbucket.scm.git.command.GitCommandBuilderFactory; -//import com.atlassian.bitbucket.scm.git.command.merge.GitMergeBaseBuilder; + /** * Determines the merge base of a pair of commits. */ -class MergeBaseResolver { - /* +/** +class MergeBaseResolver { private final GitCommandBuilderFactory builderFactory; private final GitScmConfig gitScmConfig; @@ -39,5 +38,6 @@ Commit findMergeBase(Commit a, Commit b) { return commitService.getCommit(commitRequest); } - */ -} \ No newline at end of file +} + +*/ \ No newline at end of file diff --git a/src/main/resources/atlassian-plugin.xml b/src/main/resources/atlassian-plugin.xml index 4a2e158..a6895ce 100644 --- a/src/main/resources/atlassian-plugin.xml +++ b/src/main/resources/atlassian-plugin.xml @@ -10,9 +10,7 @@ - - org.christiangalsterer.stash.filehooks.plugin.hook.ChangesetService @@ -46,6 +44,7 @@ org.christiangalsterer.stash.filehooks.plugin.hook.FileSizeHookValidator + \ No newline at end of file From 35530d51bff833d1b2f2e554e625edfe422a10e0 Mon Sep 17 00:00:00 2001 From: johnl Date: Thu, 11 Apr 2019 14:12:03 +0100 Subject: [PATCH 06/12] fix group id --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b8b0468..66820ab 100644 --- a/pom.xml +++ b/pom.xml @@ -1,7 +1,7 @@ 4.0.0 - com.openet + org.christiangalsterer bitbucket-filehooks-plugin 4.0 From f4ff75679aecd0341eb068b531bd3a3087a13f62 Mon Sep 17 00:00:00 2001 From: johnl Date: Tue, 19 Jul 2022 13:08:24 +0100 Subject: [PATCH 07/12] Update repositories and change name of module --- pom.xml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 66820ab..1e823b9 100644 --- a/pom.xml +++ b/pom.xml @@ -29,7 +29,7 @@ atlassian-plugin - 6.2.0 + 7.13.0 ${bitbucket.version} 6.3.0 1.2.3 @@ -45,6 +45,13 @@ HEAD + + + atlassian + https://packages.atlassian.com/maven-3rdparty/ + + + From a5e3190b7ed790e5ada118bc63cc6942ae9dab7a Mon Sep 17 00:00:00 2001 From: johnl Date: Mon, 25 Jul 2022 08:04:00 +0100 Subject: [PATCH 08/12] Add updates for bitbucket 8.x --- .gitignore | 2 +- pom.xml | 19 ++++- .../plugin/hook/CatFileBatchCheckHandler.java | 3 +- .../plugin/hook/ChangesetServiceImpl.java | 6 +- .../filehooks/plugin/hook/FileNameHook.java | 69 ++++++++++--------- .../plugin/hook/FileNameHookSetting.java | 6 +- 6 files changed, 61 insertions(+), 44 deletions(-) diff --git a/.gitignore b/.gitignore index ee44a96..b7e7326 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,2 @@ -.idea +.idea/* target diff --git a/pom.xml b/pom.xml index 1e823b9..9d896f0 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.christiangalsterer bitbucket-filehooks-plugin - 4.0 + 5.0 Christian Galsterer https://github.com/christiangalsterer/stash-filehooks-plugin @@ -29,7 +29,7 @@ atlassian-plugin - 7.13.0 + 8.1.1 ${bitbucket.version} 6.3.0 1.2.3 @@ -50,8 +50,21 @@ atlassian https://packages.atlassian.com/maven-3rdparty/ - + + atlassian-public + https://packages.atlassian.com/mvn/maven-external/ + + true + never + warn + + + true + warn + + + diff --git a/src/main/java/org/christiangalsterer/stash/filehooks/plugin/hook/CatFileBatchCheckHandler.java b/src/main/java/org/christiangalsterer/stash/filehooks/plugin/hook/CatFileBatchCheckHandler.java index 7935517..068bd27 100644 --- a/src/main/java/org/christiangalsterer/stash/filehooks/plugin/hook/CatFileBatchCheckHandler.java +++ b/src/main/java/org/christiangalsterer/stash/filehooks/plugin/hook/CatFileBatchCheckHandler.java @@ -5,7 +5,6 @@ import com.atlassian.bitbucket.scm.CommandInputHandler; import com.atlassian.bitbucket.scm.CommandOutputHandler; import com.atlassian.utils.process.IOUtils; -import com.atlassian.utils.process.ProcessException; import java.io.IOException; import java.io.OutputStream; @@ -32,7 +31,7 @@ public Map getOutput() { public void complete() { try { super.complete(); - } catch (ProcessException e) { + } catch (IOException e) { throw new RuntimeException(e); } } diff --git a/src/main/java/org/christiangalsterer/stash/filehooks/plugin/hook/ChangesetServiceImpl.java b/src/main/java/org/christiangalsterer/stash/filehooks/plugin/hook/ChangesetServiceImpl.java index 9881ad4..3473d8d 100644 --- a/src/main/java/org/christiangalsterer/stash/filehooks/plugin/hook/ChangesetServiceImpl.java +++ b/src/main/java/org/christiangalsterer/stash/filehooks/plugin/hook/ChangesetServiceImpl.java @@ -3,11 +3,13 @@ import com.atlassian.bitbucket.commit.Changeset; import com.atlassian.bitbucket.commit.Commit; import com.atlassian.bitbucket.content.Change; +import com.atlassian.bitbucket.repository.AbstractRefCallback; import com.atlassian.bitbucket.repository.Ref; import com.atlassian.bitbucket.repository.RefChange; import com.atlassian.bitbucket.repository.Repository; import com.atlassian.bitbucket.scm.ChangesetsCommandParameters; import com.atlassian.bitbucket.scm.CommitsCommandParameters; +import com.atlassian.bitbucket.scm.RefsCommandParameters; import com.atlassian.bitbucket.scm.ScmService; import com.atlassian.bitbucket.util.PageRequest; import com.atlassian.bitbucket.util.PageUtils; @@ -90,7 +92,9 @@ public Set getCommitsBetween(final Repository repository, Iterable getExistingRefs(final Repository repository) { Set refs = new HashSet<>(); - scmService.getCommandFactory(repository).heads(refs::add).call(); + RefsCommandParameters.Builder refsCommandParametersBuilder = new RefsCommandParameters.Builder(); + AbstractRefCallback refCallback = new AbstractRefCallback(); + scmService.getCommandFactory(repository).refs(refsCommandParametersBuilder.build(), refCallback).call(); return refs; } diff --git a/src/main/java/org/christiangalsterer/stash/filehooks/plugin/hook/FileNameHook.java b/src/main/java/org/christiangalsterer/stash/filehooks/plugin/hook/FileNameHook.java index 08bec81..dbe4ba0 100644 --- a/src/main/java/org/christiangalsterer/stash/filehooks/plugin/hook/FileNameHook.java +++ b/src/main/java/org/christiangalsterer/stash/filehooks/plugin/hook/FileNameHook.java @@ -1,39 +1,40 @@ package org.christiangalsterer.stash.filehooks.plugin.hook; -import com.atlassian.bitbucket.commit.Commit; -import com.atlassian.bitbucket.commit.CommitRequest; -import com.atlassian.bitbucket.commit.CommitService; -import com.atlassian.bitbucket.content.AbstractChangeCallback; -import com.atlassian.bitbucket.content.Change; -import com.atlassian.bitbucket.content.ChangeType; -import com.atlassian.bitbucket.content.ChangesRequest; -import com.atlassian.bitbucket.hook.HookResponse; -import com.atlassian.bitbucket.hook.repository.RepositoryHookContext; -import com.atlassian.bitbucket.hook.repository.RepositoryMergeRequestCheckContext; -import com.atlassian.bitbucket.i18n.I18nService; -import com.atlassian.bitbucket.pull.PullRequest; -import com.atlassian.bitbucket.pull.PullRequestRef; -import com.atlassian.bitbucket.repository.RefChange; -import com.atlassian.bitbucket.repository.Repository; -import com.atlassian.bitbucket.scm.pull.MergeRequest; -import com.atlassian.bitbucket.setting.Settings; -import com.atlassian.bitbucket.setting.SettingsValidationErrors; -import com.google.common.base.Strings; - -import javax.annotation.Nonnull; - -import java.io.IOException; -import java.util.Collection; -import java.util.HashSet; -import java.util.Iterator; -import java.util.Optional; -import java.util.regex.Pattern; -import java.util.regex.PatternSyntaxException; -import java.util.stream.Collectors; -import java.util.stream.StreamSupport; - -import static org.christiangalsterer.stash.filehooks.plugin.hook.Predicates.*; - +/** + * import com.atlassian.bitbucket.commit.Commit; + * import com.atlassian.bitbucket.commit.CommitRequest; + * import com.atlassian.bitbucket.commit.CommitService; + * import com.atlassian.bitbucket.content.AbstractChangeCallback; + * import com.atlassian.bitbucket.content.Change; + * import com.atlassian.bitbucket.content.ChangeType; + * import com.atlassian.bitbucket.content.ChangesRequest; + * import com.atlassian.bitbucket.hook.HookResponse; + * import com.atlassian.bitbucket.hook.repository.RepositoryHookContext; + * import com.atlassian.bitbucket.hook.repository.RepositoryMergeRequestCheckContext; + * import com.atlassian.bitbucket.i18n.I18nService; + * import com.atlassian.bitbucket.pull.PullRequest; + * import com.atlassian.bitbucket.pull.PullRequestRef; + * import com.atlassian.bitbucket.repository.RefChange; + * import com.atlassian.bitbucket.repository.Repository; + * import com.atlassian.bitbucket.scm.pull.MergeRequest; + * import com.atlassian.bitbucket.setting.Settings; + * import com.atlassian.bitbucket.setting.SettingsValidationErrors; + * import com.google.common.base.Strings; + * + * import javax.annotation.Nonnull; + * + * import java.io.IOException; + * import java.util.Collection; + * import java.util.HashSet; + * import java.util.Iterator; + * import java.util.Optional; + * import java.util.regex.Pattern; + * import java.util.regex.PatternSyntaxException; + * import java.util.stream.Collectors; + * import java.util.stream.StreamSupport; + * + * import static org.christiangalsterer.stash.filehooks.plugin.hook.Predicates.*; + */ /** * Checks the name and path of a file in the pre-receive phase and rejects the push when the changeset contains files which match the configured file name pattern. */ diff --git a/src/main/java/org/christiangalsterer/stash/filehooks/plugin/hook/FileNameHookSetting.java b/src/main/java/org/christiangalsterer/stash/filehooks/plugin/hook/FileNameHookSetting.java index 4105784..feab7d8 100644 --- a/src/main/java/org/christiangalsterer/stash/filehooks/plugin/hook/FileNameHookSetting.java +++ b/src/main/java/org/christiangalsterer/stash/filehooks/plugin/hook/FileNameHookSetting.java @@ -7,9 +7,9 @@ class FileNameHookSetting { - private Pattern includePattern; - private Optional excludePattern; - private Optional branchesPattern; + private final Pattern includePattern; + private final Optional excludePattern; + private final Optional branchesPattern; FileNameHookSetting(String includePattern, String excludePattern, String branchesPattern) { this.includePattern = Pattern.compile(includePattern); From 3fba84a4a9b7aa0aa24d9ba2ee4f3485a2d69852 Mon Sep 17 00:00:00 2001 From: johnl Date: Mon, 25 Jul 2022 08:22:16 +0100 Subject: [PATCH 09/12] Updates as per review from mkgl --- .gitignore | 1 + .idea/compiler.xml | 13 ------------- pom.xml | 2 +- 3 files changed, 2 insertions(+), 14 deletions(-) delete mode 100644 .idea/compiler.xml diff --git a/.gitignore b/.gitignore index b7e7326..0628185 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ .idea/* target +*.iml diff --git a/.idea/compiler.xml b/.idea/compiler.xml deleted file mode 100644 index 460d80e..0000000 --- a/.idea/compiler.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/pom.xml b/pom.xml index 9d896f0..cf4663a 100644 --- a/pom.xml +++ b/pom.xml @@ -88,7 +88,7 @@ com.atlassian.bitbucket.server - bitbucket-scm-common + bitbucket-scm-common ${bitbucket.version} provided From ffa1298e3414389dffab3e75cd5c22ea1b11855b Mon Sep 17 00:00:00 2001 From: johnl Date: Wed, 27 Jul 2022 19:55:03 +0100 Subject: [PATCH 10/12] Fix empty refs returned, add logging, implement code improvement suggestions from IDEA, tidy up, bump version --- pom.xml | 4 +- .../plugin/hook/CachingResolver.java | 2 +- .../plugin/hook/CatFileBatchCheckHandler.java | 7 +++- .../plugin/hook/ChangesetServiceImpl.java | 42 +++++++++++++------ .../filehooks/plugin/hook/FileSizeHook.java | 29 +++++++++---- .../plugin/hook/FileSizeHookSetting.java | 4 +- .../plugin/hook/FileSizeHookValidator.java | 13 +++--- .../plugin/hook/FirstLineOutputHandler.java | 19 +++++---- .../filehooks/plugin/hook/Functions.java | 8 ++-- .../plugin/hook/FileNameHookTest.java | 8 ++-- 10 files changed, 86 insertions(+), 50 deletions(-) diff --git a/pom.xml b/pom.xml index cf4663a..45ee654 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.christiangalsterer bitbucket-filehooks-plugin - 5.0 + 5.0.4 Christian Galsterer https://github.com/christiangalsterer/stash-filehooks-plugin @@ -31,7 +31,7 @@ 8.1.1 ${bitbucket.version} - 6.3.0 + 8.0.0 1.2.3 3.3 3.1.0 diff --git a/src/main/java/org/christiangalsterer/stash/filehooks/plugin/hook/CachingResolver.java b/src/main/java/org/christiangalsterer/stash/filehooks/plugin/hook/CachingResolver.java index 81fab7b..e12b2d1 100644 --- a/src/main/java/org/christiangalsterer/stash/filehooks/plugin/hook/CachingResolver.java +++ b/src/main/java/org/christiangalsterer/stash/filehooks/plugin/hook/CachingResolver.java @@ -19,7 +19,7 @@ * @param type of value elements to be stored in cache */ public class CachingResolver { - private Map cache = new HashMap<>(); + private final Map cache = new HashMap<>(); /** * Resolves requested key to a previously cached value. diff --git a/src/main/java/org/christiangalsterer/stash/filehooks/plugin/hook/CatFileBatchCheckHandler.java b/src/main/java/org/christiangalsterer/stash/filehooks/plugin/hook/CatFileBatchCheckHandler.java index 068bd27..897c3bc 100644 --- a/src/main/java/org/christiangalsterer/stash/filehooks/plugin/hook/CatFileBatchCheckHandler.java +++ b/src/main/java/org/christiangalsterer/stash/filehooks/plugin/hook/CatFileBatchCheckHandler.java @@ -5,7 +5,10 @@ import com.atlassian.bitbucket.scm.CommandInputHandler; import com.atlassian.bitbucket.scm.CommandOutputHandler; import com.atlassian.utils.process.IOUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import javax.annotation.Nonnull; import java.io.IOException; import java.io.OutputStream; import java.nio.charset.StandardCharsets; @@ -22,6 +25,8 @@ public class CatFileBatchCheckHandler extends LineReaderOutputHandler implements this.changesets = changesets; } + private static final Logger LOGGER = LoggerFactory.getLogger(CatFileBatchCheckHandler.class); + @Override public Map getOutput() { return values; @@ -49,7 +54,7 @@ protected void processReader(LineReader reader) throws IOException { } @Override - public void process(OutputStream input) { + public void process(@Nonnull OutputStream input) { try { for (String c : changesets) { input.write(c.getBytes(StandardCharsets.UTF_8.name())); diff --git a/src/main/java/org/christiangalsterer/stash/filehooks/plugin/hook/ChangesetServiceImpl.java b/src/main/java/org/christiangalsterer/stash/filehooks/plugin/hook/ChangesetServiceImpl.java index 3473d8d..2606872 100644 --- a/src/main/java/org/christiangalsterer/stash/filehooks/plugin/hook/ChangesetServiceImpl.java +++ b/src/main/java/org/christiangalsterer/stash/filehooks/plugin/hook/ChangesetServiceImpl.java @@ -3,10 +3,7 @@ import com.atlassian.bitbucket.commit.Changeset; import com.atlassian.bitbucket.commit.Commit; import com.atlassian.bitbucket.content.Change; -import com.atlassian.bitbucket.repository.AbstractRefCallback; -import com.atlassian.bitbucket.repository.Ref; -import com.atlassian.bitbucket.repository.RefChange; -import com.atlassian.bitbucket.repository.Repository; +import com.atlassian.bitbucket.repository.*; import com.atlassian.bitbucket.scm.ChangesetsCommandParameters; import com.atlassian.bitbucket.scm.CommitsCommandParameters; import com.atlassian.bitbucket.scm.RefsCommandParameters; @@ -15,7 +12,11 @@ import com.atlassian.bitbucket.util.PageUtils; import com.atlassian.bitbucket.util.PagedIterable; import com.google.common.collect.Iterables; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import javax.annotation.Nonnull; +import java.io.IOException; import java.util.*; import java.util.stream.Collectors; import java.util.stream.StreamSupport; @@ -25,6 +26,7 @@ public class ChangesetServiceImpl implements ChangesetService { private static final PageRequest PAGE_REQUEST = PageUtils.newRequest(0, PageRequest.MAX_PAGE_LIMIT); private static final int MAX_CHANGES_PER_COMMIT = PageRequest.MAX_PAGE_LIMIT; + private static final Logger LOGGER = LoggerFactory.getLogger(ChangesetServiceImpl.class); private final ScmService scmService; public ChangesetServiceImpl(ScmService scmService) { @@ -33,6 +35,7 @@ public ChangesetServiceImpl(ScmService scmService) { @Override public Iterable getChanges(Iterable refChanges, final Repository repository) { + LOGGER.info("Getting changes as iterable"); List changes = new ArrayList<>(); Iterable commits = getCommitsBetween(repository, refChanges); @@ -40,23 +43,27 @@ public Iterable getChanges(Iterable refChanges, final Reposit Iterables.addAll(changes, values); } + LOGGER.info("Returning iterable changes"); return changes; } @Override public Map> getChanges(final Repository repository, Iterable commits) { + LOGGER.info("Getting changes as map"); Map> changesByCommit = new HashMap<>(); Iterable changesets = getChangesets(repository, commits); for (Changeset changeset : changesets) { - changesByCommit.put(changeset.getToCommit(), changeset.getChanges().getValues()); + changesByCommit.put(changeset.getToCommit(), Objects.requireNonNull(changeset.getChanges()).getValues()); } - + LOGGER.info("Changes by commit : " + changesByCommit.size()); + LOGGER.info("Returning map of changes"); return changesByCommit; } @Override public Set getCommitsBetween(final Repository repository, Iterable refChanges) { + LOGGER.info("Getting commits between"); Set commits = new HashSet<>(); CommitsCommandParameters.Builder builder = new CommitsCommandParameters.Builder().withMessages(false); @@ -87,33 +94,44 @@ public Set getCommitsBetween(final Repository repository, Iterable getExistingRefs(final Repository repository) { + LOGGER.info("Getting refs"); Set refs = new HashSet<>(); - RefsCommandParameters.Builder refsCommandParametersBuilder = new RefsCommandParameters.Builder(); - AbstractRefCallback refCallback = new AbstractRefCallback(); - scmService.getCommandFactory(repository).refs(refsCommandParametersBuilder.build(), refCallback).call(); + RefCallback refCallback = new RefCallback() { + @Override + public boolean onRef(@Nonnull Ref ref) throws IOException { + refs.add(ref); + return true; + } + }; + scmService.getCommandFactory(repository).refs(new RefsCommandParameters.Builder().build(), refCallback).call(); + + LOGGER.info("Returning refs, count: " + refs.size()); return refs; } private Iterable getChangesets(final Repository repository, Iterable commits) { + LOGGER.info("Getting change sets"); Iterable changesets = new ArrayList<>(); final Collection commitIds = StreamSupport.stream(commits.spliterator(), false) .map(Commit::getId) .collect(Collectors.toSet()); - + LOGGER.info("Number of commit ids: " + commitIds.size()); if (!commitIds.isEmpty()) { - changesets = new PagedIterable<>(pageRequest -> scmService.getCommandFactory(repository).changesets( + changesets = new PagedIterable<>(pageRequest -> Objects.requireNonNull(scmService.getCommandFactory(repository).changesets( new ChangesetsCommandParameters.Builder() .commitIds(commitIds) .maxChangesPerCommit(MAX_CHANGES_PER_COMMIT) .maxMessageLength(0) .build(), - pageRequest).call(), PAGE_REQUEST); + pageRequest).call()), PAGE_REQUEST); } + LOGGER.info("Returning change sets"); return changesets; } } diff --git a/src/main/java/org/christiangalsterer/stash/filehooks/plugin/hook/FileSizeHook.java b/src/main/java/org/christiangalsterer/stash/filehooks/plugin/hook/FileSizeHook.java index e15943e..5f2b476 100644 --- a/src/main/java/org/christiangalsterer/stash/filehooks/plugin/hook/FileSizeHook.java +++ b/src/main/java/org/christiangalsterer/stash/filehooks/plugin/hook/FileSizeHook.java @@ -9,6 +9,9 @@ import com.atlassian.bitbucket.scm.ScmCommandBuilder; import com.atlassian.bitbucket.scm.ScmService; import com.atlassian.bitbucket.setting.Settings; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import javax.annotation.Nonnull; import java.util.*; import java.util.regex.Pattern; @@ -24,6 +27,7 @@ */ public class FileSizeHook implements PreRepositoryHook { + private static final Logger LOGGER = LoggerFactory.getLogger(FileSizeHook.class); private static final int MAX_SETTINGS = 5; private static final String SETTINGS_INCLUDE_PATTERN_PREFIX = "pattern-"; private static final String SETTINGS_EXCLUDE_PATTERN_PREFIX = "pattern-exclude-"; @@ -40,6 +44,7 @@ public FileSizeHook(ChangesetService changesetService, ScmService scmService) { private List getSettings(Settings settings) { + LOGGER.info("Get hook configuration settings"); List configurations = new ArrayList<>(); String includeRegex; Long size; @@ -55,24 +60,26 @@ private List getSettings(Settings settings) { configurations.add(new FileSizeHookSetting(size, includeRegex, excludeRegex, branchesRegex)); } } - + LOGGER.info("Return hook configuration settings"); return configurations; } private Map getSizeForContentIds(final Repository repository, Iterable contentIds) { - ScmCommandBuilder scmCommandBuilder = scmService.createBuilder(repository); + LOGGER.info("Get size for content ids"); + ScmCommandBuilder scmCommandBuilder = scmService.createBuilder(repository); CatFileBatchCheckHandler handler = new CatFileBatchCheckHandler(contentIds); Command> cmd = scmCommandBuilder .command("cat-file") .argument("--batch-check") .inputHandler(handler) .build(handler); - return filterOutNullSizes(cmd.call()); + return filterOutNullSizes(Objects.requireNonNull(cmd.call())); } private Map filterOutNullSizes(Map sizes) { + LOGGER.info("Filter null sizes"); return sizes.entrySet() .stream() .filter(entry -> entry.getValue() != null) @@ -84,6 +91,7 @@ private Map filterOutNullSizes(Map sizes) { @Override public RepositoryHookResult preUpdate(@Nonnull PreRepositoryHookContext context, @Nonnull RepositoryHookRequest request) { + LOGGER.info("Start of preUpdate hook event"); Repository repository = request.getRepository(); List settings = getSettings(context.getSettings()); @@ -107,6 +115,7 @@ public RepositoryHookResult preUpdate(@Nonnull PreRepositoryHookContext context, Set commits = changesetService.getCommitsBetween(repository, filteredRefChanges.collect(Collectors.toSet())); + LOGGER.info("Number of commits: " + commits.size()); Set filteredChanges = changesByCommit.flatBatchResolve(commits, x -> changesetService.getChanges(repository, x)).stream() @@ -118,7 +127,7 @@ public RepositoryHookResult preUpdate(@Nonnull PreRepositoryHookContext context, || !setting.getExcludePattern().get().matcher(fullPath).find()); }) .collect(Collectors.toSet()); - + LOGGER.info("Number of filtered changes: " + filteredChanges.size()); // Pre-populate cache by resolving all required changes at once sizesByContentId.batchResolve( filteredChanges.stream().map(Change::getContentId).collect(Collectors.toSet()), @@ -128,16 +137,19 @@ public RepositoryHookResult preUpdate(@Nonnull PreRepositoryHookContext context, .filter(change -> sizesByContentId.resolve(change.getContentId()) > maxFileSize) .map(change -> change.getPath().toString()) .collect(Collectors.toList()); - + LOGGER.info("Violating paths: " + violatingPaths.size() + ", filtered paths: " + filteredPaths.size()); addAll(violatingPaths, filteredPaths); if (pathAndSizes.containsKey(maxFileSize)) addAll(violatingPaths, pathAndSizes.get(maxFileSize)); pathAndSizes.put(maxFileSize, violatingPaths); + + for (Map.Entry> entry : pathAndSizes.entrySet()) { + LOGGER.info("File size : " + entry.getKey() + ", file path: " + entry.getValue()); + } } - RepositoryHookResult result; ArrayList resultList = new ArrayList<>(); boolean hookPassed = true; @@ -149,13 +161,14 @@ public RepositoryHookResult preUpdate(@Nonnull PreRepositoryHookContext context, resultList.add(String.format("File [%s] is too large. Maximum allowed file size is %s bytes.", path, maxFileSize)); } } - + LOGGER.info("End of preUpdate repo hook event, hook passed: " + hookPassed); if (hookPassed) { return RepositoryHookResult.accepted(); } else { - return RepositoryHookResult.rejected("files are too large", Arrays.toString(resultList.toArray())); + return RepositoryHookResult.rejected("Files are too large", Arrays.toString(resultList.toArray())); } + } } diff --git a/src/main/java/org/christiangalsterer/stash/filehooks/plugin/hook/FileSizeHookSetting.java b/src/main/java/org/christiangalsterer/stash/filehooks/plugin/hook/FileSizeHookSetting.java index 7da53a4..d1d89c1 100644 --- a/src/main/java/org/christiangalsterer/stash/filehooks/plugin/hook/FileSizeHookSetting.java +++ b/src/main/java/org/christiangalsterer/stash/filehooks/plugin/hook/FileSizeHookSetting.java @@ -7,8 +7,8 @@ class FileSizeHookSetting { - private Long size; - private Pattern includePattern; + private final Long size; + private final Pattern includePattern; private Optional excludePattern; private Optional branchesPattern; diff --git a/src/main/java/org/christiangalsterer/stash/filehooks/plugin/hook/FileSizeHookValidator.java b/src/main/java/org/christiangalsterer/stash/filehooks/plugin/hook/FileSizeHookValidator.java index 8a8a9ad..5cd248d 100644 --- a/src/main/java/org/christiangalsterer/stash/filehooks/plugin/hook/FileSizeHookValidator.java +++ b/src/main/java/org/christiangalsterer/stash/filehooks/plugin/hook/FileSizeHookValidator.java @@ -1,14 +1,16 @@ package org.christiangalsterer.stash.filehooks.plugin.hook; import com.atlassian.bitbucket.i18n.I18nService; -import com.atlassian.bitbucket.repository.Repository; import com.atlassian.bitbucket.scope.Scope; -import com.atlassian.bitbucket.setting.SettingsValidator; import com.atlassian.bitbucket.setting.Settings; import com.atlassian.bitbucket.setting.SettingsValidationErrors; +import com.atlassian.bitbucket.setting.SettingsValidator; import com.google.common.base.Strings; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import javax.annotation.Nonnull; +import java.util.Objects; import java.util.Set; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; @@ -21,6 +23,7 @@ public class FileSizeHookValidator implements SettingsValidator { private static final String SETTINGS_SIZE_PREFIX = "size-"; private static final String SETTINGS_BRANCHES_PATTERN_PREFIX = "pattern-branches-"; + private static final Logger LOGGER = LoggerFactory.getLogger(FileSizeHookValidator.class); private final I18nService i18n; public FileSizeHookValidator(I18nService i18n) { @@ -29,14 +32,12 @@ public FileSizeHookValidator(I18nService i18n) { @Override public void validate(@Nonnull Settings settings, @Nonnull SettingsValidationErrors errors, @Nonnull Scope scope) { - int patternParams = 0; final Set params = settings.asMap().keySet(); for (String param : params) { if (param.matches(SETTINGS_INCLUDE_PATTERN_PREFIX + "[1-5]$")) { patternParams++; - continue; } } @@ -65,7 +66,7 @@ public void validate(@Nonnull Settings settings, @Nonnull SettingsValidationErro if (!Strings.isNullOrEmpty(settings.getString(SETTINGS_EXCLUDE_PATTERN_PREFIX + i))) { try { - Pattern.compile(settings.getString(SETTINGS_EXCLUDE_PATTERN_PREFIX + i)); + Pattern.compile(Objects.requireNonNull(settings.getString(SETTINGS_EXCLUDE_PATTERN_PREFIX + i))); } catch (PatternSyntaxException e) { errors.addFieldError(SETTINGS_EXCLUDE_PATTERN_PREFIX + i, i18n.getText("filesize-hook.error.pattern", "Pattern is not a valid regular expression")); } @@ -73,7 +74,7 @@ public void validate(@Nonnull Settings settings, @Nonnull SettingsValidationErro if (!Strings.isNullOrEmpty(settings.getString(SETTINGS_BRANCHES_PATTERN_PREFIX + i))) { try { - Pattern.compile(settings.getString(SETTINGS_BRANCHES_PATTERN_PREFIX + i)); + Pattern.compile(Objects.requireNonNull(settings.getString(SETTINGS_BRANCHES_PATTERN_PREFIX + i))); } catch (PatternSyntaxException e) { errors.addFieldError(SETTINGS_BRANCHES_PATTERN_PREFIX + i, i18n.getText("filesize-hook.error.pattern", "Pattern is not a valid regular expression")); } diff --git a/src/main/java/org/christiangalsterer/stash/filehooks/plugin/hook/FirstLineOutputHandler.java b/src/main/java/org/christiangalsterer/stash/filehooks/plugin/hook/FirstLineOutputHandler.java index 2e269db..dc5e7c7 100644 --- a/src/main/java/org/christiangalsterer/stash/filehooks/plugin/hook/FirstLineOutputHandler.java +++ b/src/main/java/org/christiangalsterer/stash/filehooks/plugin/hook/FirstLineOutputHandler.java @@ -1,21 +1,22 @@ package org.christiangalsterer.stash.filehooks.plugin.hook; +import com.atlassian.bitbucket.io.LineReader; +import com.atlassian.bitbucket.io.LineReaderOutputHandler; +import com.atlassian.bitbucket.scm.CommandOutputHandler; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.annotation.Nullable; import java.io.IOException; -import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; - -import javax.annotation.Nullable; - -import com.atlassian.bitbucket.io.LineReader; -import com.atlassian.bitbucket.io.LineReaderOutputHandler; -import com.atlassian.bitbucket.scm.CommandOutputHandler; /** * Returns the first line of output provided by the git process. */ public class FirstLineOutputHandler extends LineReaderOutputHandler implements - CommandOutputHandler { - + CommandOutputHandler { + + private static final Logger LOGGER = LoggerFactory.getLogger(FirstLineOutputHandler.class); private String sha; FirstLineOutputHandler() { diff --git a/src/main/java/org/christiangalsterer/stash/filehooks/plugin/hook/Functions.java b/src/main/java/org/christiangalsterer/stash/filehooks/plugin/hook/Functions.java index b32f97b..aa00558 100644 --- a/src/main/java/org/christiangalsterer/stash/filehooks/plugin/hook/Functions.java +++ b/src/main/java/org/christiangalsterer/stash/filehooks/plugin/hook/Functions.java @@ -1,16 +1,14 @@ package org.christiangalsterer.stash.filehooks.plugin.hook; -import com.atlassian.bitbucket.content.Change; import com.atlassian.bitbucket.commit.Commit; -import com.atlassian.bitbucket.commit.Changeset; -import com.atlassian.bitbucket.repository.RefChange; -import com.atlassian.bitbucket.repository.RefChangeType; +import com.atlassian.bitbucket.commit.MinimalCommit; +import com.atlassian.bitbucket.content.Change; import java.util.function.Function; class Functions { - static final Function COMMIT_TO_COMMIT_ID = commit -> commit.getId(); + static final Function COMMIT_TO_COMMIT_ID = MinimalCommit::getId; static final Function CHANGE_TO_PATH = change -> change.getPath().toString(); } diff --git a/src/test/java/org/christiangalsterer/stash/filehooks/plugin/hook/FileNameHookTest.java b/src/test/java/org/christiangalsterer/stash/filehooks/plugin/hook/FileNameHookTest.java index 2302cec..9153f14 100644 --- a/src/test/java/org/christiangalsterer/stash/filehooks/plugin/hook/FileNameHookTest.java +++ b/src/test/java/org/christiangalsterer/stash/filehooks/plugin/hook/FileNameHookTest.java @@ -1,18 +1,18 @@ package org.christiangalsterer.stash.filehooks.plugin.hook; +/* import com.atlassian.bitbucket.commit.CommitService; import com.atlassian.bitbucket.i18n.I18nService; import com.atlassian.bitbucket.repository.Repository; +import com.atlassian.bitbucket.scm.git.command.GitCommandBuilderFactory; import com.atlassian.bitbucket.setting.Settings; - -/* import org.junit.Before; import org.junit.Test; import org.mockito.Mock; -import static org.junit.Assert.*; -import static org.mockito.MockitoAnnotations.initMocks; import static org.hamcrest.core.Is.is; +import static org.junit.Assert.assertThat; +import static org.mockito.MockitoAnnotations.initMocks; public class FileNameHookTest { From 8432042d3efcb39f02ff3386553913f72deb59ce Mon Sep 17 00:00:00 2001 From: johnl Date: Thu, 28 Jul 2022 08:54:16 +0100 Subject: [PATCH 11/12] Update release info, add developer info --- README.md | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/README.md b/README.md index 3072d1c..df163b5 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,25 @@ The following example rejects all files matching the pattern **readme.md** when ![File Size Hook Configuration](screenshots/file-hooks-plugin-filename-hook-configuration.png) # Releases +5.0.4 + +Fix bug where refs were not collected properly + +5.0.3 + +Code improvements as suggested by Intellij + +5.0.2 + +Add logging support via logback, logs will now be written to atlassian-bitbucket.log when debug is enabled in the Bitbucket GUI + +5.0.1 + +Minor updates + +5.0.0 + +Updates to support Bitbucket 8.x 3.3.2 (2018-04-15) @@ -108,6 +127,32 @@ The following example rejects all files matching the pattern **readme.md** when * Reject commits containing files which exceed a configurable file size. Files can be identified by regular expressions. +# Developer Guide + +### Building the plugin + +The following tools are required to build the plugin: +``` +- java 1.8 +- atlassian sdk +``` +Instructions on how to install the Atlassian SDK can be found [here](https://developer.atlassian.com/server/framework/atlassian-sdk/install-the-atlassian-sdk-on-a-linux-or-mac-system/) + +Clone the repository: + +``` +git clone https://github.com/christiangalsterer/stash-filehooks-plugin.git +``` +Edit the pom.xml in the root of the checkout, and change the line (change to match the version of Bitbucket you want to build against): + +``` +7.13.0 +``` +Build the plugin +``` +atlas-mvn clean install +``` + # Roadmap From f8e1b9a82ef0ef4903441189cfe8df6ddcf73e39 Mon Sep 17 00:00:00 2001 From: John Lawlor Date: Thu, 28 Jul 2022 09:00:08 +0100 Subject: [PATCH 12/12] Update README.md Add release dates --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index df163b5..3991825 100644 --- a/README.md +++ b/README.md @@ -25,23 +25,23 @@ The following example rejects all files matching the pattern **readme.md** when ![File Size Hook Configuration](screenshots/file-hooks-plugin-filename-hook-configuration.png) # Releases -5.0.4 +5.0.4 (2022-07-28) Fix bug where refs were not collected properly -5.0.3 +5.0.3 (2022-07-27) Code improvements as suggested by Intellij -5.0.2 +5.0.2 (2022-07-22) Add logging support via logback, logs will now be written to atlassian-bitbucket.log when debug is enabled in the Bitbucket GUI -5.0.1 +5.0.1 (2022-07-20) Minor updates -5.0.0 +5.0.0 (2022-07-01) Updates to support Bitbucket 8.x