StreamFile not recognized as method in custom script hook?

I'm trying to implement a pre-receive hook in Bitbucket Server using the Custom Script Hook feature. I'm not the most savvy with Groovy, but I was fairly sure I'd got the syntax right. However, on trying to add the hook it gives the error:

 

[Static type checking] - Cannot find matching method com.atlassian.bitbucket.content.ContentServce#streamFile

 

import com.atlassian.sal.api.component.ComponentLocator
import com.atlassian.bitbucket.hook.HookResponse
import com.atlassian.bitbucket.repository.RefChange
import com.atlassian.bitbucket.repository.Repository
import com.atlassian.bitbucket.content.ContentService
import com.atlassian.bitbucket.content.FileContentCallback

if(repository.getProject().getName() == "Constellation" && repository.getName() == "apollo" && refChanges.pathsMatch('glob:apollo/ui/**')) {
    def contentService = ComponentLocator.getComponent(ContentService)
    def filePath = "apollo/ui/dist/js/app.js"
    def searchFragment = "WSURL=window.location.hostname"
    def fragmentFound = false
    contentService.streamFile(repository, refChanges.first().getToHash(), filePath, new FileContentCallback() {
        @Override
        boolean onLine(int lineNumber, String line, boolean truncated) throws IOException {
            if(line.toLowerCase().contains(searchFragment.toLowerCase()))
                fragmentFound = true
            return true;
        }
    });
    if(!fragmentFound) {
        //reject push with note
        hookResponse.out().print("Push rejected!\nAre you sure you ran `npm run build`?\n\n")
        return true
    } else {
        //allow push
        return false
    }
}

 

I've imported ContentService and it does seem to recognize ContentService when obtained with ComponentLocator. So why doesn't it recognize the streamFile method?

 

Any other comments on my code are welcome as well. The purpose of the pre-receive hook is to only accept pushes that have edits to files in a certain folder if a specific file has a certain substring. The API seems to say that ContentService.streamFile is the correct method to read a whole source file, as opposed to just reading changes from a given commit.

 

Thanks in advance!

2 answers

1 accepted

2 votes
Answer accepted

Theres a siimilar question here regarding reading files in a repository using ScriptRunner: https://answers.atlassian.com/questions/39450211

Your code should look like this:

import com.atlassian.bitbucket.content.ContentService
import com.atlassian.bitbucket.io.TypeAwareOutputSupplier
import com.atlassian.sal.api.component.ComponentLocator

import javax.annotation.Nonnull
import java.nio.charset.StandardCharsets

if(repository.getProject().getName() == "Constellation" && repository.getName() == "apollo" && refChanges.pathsMatch('glob:apollo/ui/**')) {
    def contentService = ComponentLocator.getComponent(ContentService)
    def filePath = "apollo/ui/dist/js/app.js"
    def searchFragment = "WSURL=window.location.hostname"

    def supplier = new ByteArrayStreamOutputSupplier()

    contentService.streamFile(repository, refChanges.first().getToHash(), filePath, supplier)

    supplier.stream.close()

    // convert byte array to UTF-8 encoded string which has the file contents
    def content = new String(supplier.baos.toByteArray(), StandardCharsets.UTF_8)

    // process each line in the content
    def fragmentFound = content.readLines().any { line ->
        line.toLowerCase().contains(searchFragment.toLowerCase())
    }

    if(!fragmentFound) {
        //reject push with note
        hookResponse.out().print("Push rejected!\nAre you sure you ran `npm run build`?\n\n")
        return false
    }
}

return true

class ByteArrayStreamOutputSupplier implements TypeAwareOutputSupplier {

    ByteArrayOutputStream baos
    BufferedOutputStream stream

    ByteArrayStreamOutputSupplier() {
        this.baos = new ByteArrayOutputStream()
        this.stream = new BufferedOutputStream(baos)
    }

    @Override
    OutputStream getStream(@Nonnull String contentType) throws IOException {
        stream
    }
}

Issues I can see are:

  • You need to return false if you want to block the push and true if you want to allow the push.
  • ContentService looks like its spelt wrong in that error message, could that be the issue?
  • Your best off using the TypeAwareOutputSupplier version of stream file, its just easier to use in my view.
  • May be best off using repository.getSlug() and project.getKey() rather than the names.

You'll see a static type checking error in the inline code editor but its safe to ignore, it just can't work out where the pathsMatch method comes from, which is one ScriptRunner provides.

For future reference if you tag your question with: com.onresolve.jira.stash.groovyrunner its easier for us to respond and means your question will be found by the right person.

Thanks for the response! I'll try out your changes and see if they work. The misspelled error message was my bad, not related to the code.

Suggest an answer

Log in or Sign up to answer
Community showcase
Posted 6 hours ago in United States

Atlassian acquires AgileCraft

         Good Day, Bad bad traffic, not sure why!!!! 1/2 hour commute took me 2 hours today 🤯 What helped me is that I kept browsing LinkedIn until...

31 views 1 0
View post

Atlassian User Groups

Connect with like-minded Atlassian users at free events near you!

Find a group

Connect with like-minded Atlassian users at free events near you!

Find my local user group

Unfortunately there are no AUG chapters near you at the moment.

Start an AUG

You're one step closer to meeting fellow Atlassian users at your local meet up. Learn more about AUGs

Groups near you