Groovy пробел многострочной интерполяции строк - PullRequest
1 голос
/ 02 апреля 2020

Я пытаюсь сгенерировать какой-нибудь общий c Groovy код для Jenkins, но у меня, похоже, проблемы с многострочными строками и дополнительным пробелом. Я перепробовал все, что смог найти с помощью Google, но я не могу заставить его работать.

Моя проблема не связана с простыми многострочными строками. Мне удалось обрезать пустое пространство с помощью методов stripIndent () и stripMargin () для простых случаев. Моя проблема вызвана интерполяцией методов внутри моих строк.

Groovy info: Groovy Version: 3.0.2 JVM: 13.0.2 Vendor: Oracle Corporation OS: Mac OS X

String method2(String tier, String jobName) {
    return """
            Map downstreamJobs = [:]
            stage ("${jobName}-${tier}-\${region}_${jobName}") {
                test
            }
        """.stripIndent().stripMargin()
}

static String simpleLog() {
    return """
            script {
               def user = env.BUILD_USER_ID
            }
          """.stripIndent().stripMargin()
}

static String method1() {
    return """\
            import jenkins.model.Jenkins
            currentBuild.displayName = "name"

            ${simpleLog()}
        """.stripIndent().stripMargin()
}

String generateFullDeploymentPipelineCode() {
    return """Text here
            ${method1()}
            ${method2("test1", "test2")}
            """.stripIndent().stripMargin()
}

println(generateFullDeploymentPipelineCode())

Это то, что он печатает (или записывает на диск):

Text here
                      import jenkins.model.Jenkins
          currentBuild.displayName = "name"

script {
   def user = env.BUILD_USER_ID
}



Map downstreamJobs = [:]
stage ("test2-test1-${region}_test2") {
    test
}

Почему дополнительное пространство вокруг строк импорта? Я знаю, что метод отступа должен обрезать все пробелы в соответствии с наименьшим количеством начальных пробелов, поэтому мы используем backsla sh (пример здесь { ссылка }).

Это работает для простых строк, но не работает, если использовать start с использованием интерполяции. Не с обычными переменными, просто когда вы интерполируете весь метод.

Ответы [ 2 ]

2 голосов
/ 03 апреля 2020

как вариант - используйте только stripMargin() и только один раз для окончательной строки

String method2(String tier, String jobName) {
    return """\
            |Map downstreamJobs = [:]
            |stage ("${jobName}-${tier}-\${region}_${jobName}") {
            |    test
            |}
        """
}

static String simpleLog() {
    return """\
            |script {
            |   def user = env.BUILD_USER_ID
            |}
          """
}

static String method1() {
    return """\
            |import jenkins.model.Jenkins
            |currentBuild.displayName = "name"

            ${simpleLog()}
        """
}

String generateFullDeploymentPipelineCode() {
    return """\
            |Text here
            ${method1()}
            ${method2("test1", "test2")}
            """.stripIndent().stripMargin()
}

println(generateFullDeploymentPipelineCode())

результат:

Text here
import jenkins.model.Jenkins
currentBuild.displayName = "name"

script {
   def user = env.BUILD_USER_ID
}

Map downstreamJobs = [:]
stage ("test2-test1-${region}_test2") {
    test
}

другой вариант с trim () и stripIndent ( )

def method2(String tier, String jobName) {
    return """
            Map downstreamJobs = [:]
            stage ("${jobName}-${tier}-\${region}_${jobName}") {
                test
            }
        """.trim()
}

def simpleLog() {
    return """
            script {
               def user = env.BUILD_USER_ID
            }
          """.trim()
}

def method1() {
    return """
            import jenkins.model.Jenkins
            currentBuild.displayName = "name"
            ${simpleLog()}
        """.trim()
}

def generateFullDeploymentPipelineCode() {
    return """\
            Text here
            ${method1()}
            ${method2("test1", "test2")}
            """.stripIndent()
}

println(generateFullDeploymentPipelineCode())
1 голос
/ 03 апреля 2020

Когда вы вставляете строку с помощью интерполяции, вы делаете отступ только в первой строке. Следующие строки вставленной строки будут иметь разные отступы, что испортит все.

Используя некоторые менее известные члены GString (а именно .strings[] и .values[]), мы можем выровнять отступ все строки каждого интерполированного значения.

String method2(String tier, String jobName) {
    indented """
        Map downstreamJobs = [:]
        stage ("${jobName}-${tier}-\${region}_${jobName}") {
            test
        }
    """
}

String simpleLog() {
    indented """\
        script {
           def user = env.BUILD_USER_ID
        }
    """
}

String method1() {
    indented """\
        import jenkins.model.Jenkins
        currentBuild.displayName = "name"

        ${simpleLog()}
    """
}

String generateFullDeploymentPipelineCode() {
    indented """\
        Text here
        ${method1()}
        ${method2("test1", "test2")}
    """
}

println generateFullDeploymentPipelineCode()

//---------- Move the following code into its own script ----------

// Function to adjust the indentation of interpolated values so that all lines
// of a value match the indentation of the first line.
// Finally stripIndent() will be called before returning the string.

String indented( GString templ ) {

    // Iterate over the interpolated values of the GString template.
    templ.values.eachWithIndex{ value, i ->

        // Get the string preceding the current value. Always defined, even
        // when the value is at the beginning of the template.
        def beforeValue = templ.strings[ i ]

        // RegEx to match any indent substring before the value.
        // Special case for the first string, which doesn't necessarily contain '\n'. 
        def regexIndent = i == 0
                          ? /(?:^|\n)([ \t]+)$/
                          : /\n([ \t]+)$/

        def matchIndent = ( beforeValue =~ regexIndent )
        if( matchIndent ) {
            def indent = matchIndent[ 0 ][ 1 ]
            def lines = value.readLines()
            def linesNew = [ lines.head() ]  // The 1st line is already indented.
            // Insert the indentation from the 1st line into all subsequent lines.
            linesNew += lines.tail().collect{ indent + it }
            // Finally replace the value with the reformatted lines.
            templ.values[ i ] = linesNew.join('\n')
        }
    }

    return templ.stripIndent()
}

// Fallback in case the input string is not a GString (when it doesn't contain expressions)
String indented( String templ ) {
    return templ.stripIndent()  
}

Прямая демонстрация на площадке кодирования

Вывод:

Text here
import jenkins.model.Jenkins
currentBuild.displayName = "name"

script {
   def user = env.BUILD_USER_ID
}

Map downstreamJobs = [:]
stage ("test2-test1-${region}_test2") {
    test
}

Заключение:

Используя функцию indented, был достигнут чистый синтаксис Groovy для генерации кода из шаблонов GString.

Это был довольно познавательный опыт. Сначала я попытался сделать это совершенно по-другому, используя функцию evaluate, которая оказалась слишком сложной и не такой гибкой. Затем я случайно просмотрел несколько постов из блога mrhaki (всегда хорошее чтение!), Пока не обнаружил "Groovy Goodness: узнайте больше о GString" . Это был ключ к реализации этого решения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...