From 0d7389e28a9ae6f3f275761bc3f8f1844956a434 Mon Sep 17 00:00:00 2001 From: Mukul Sharma Date: Wed, 2 Sep 2026 18:06:49 +0530 Subject: [PATCH] Fix IllegalArgumentException in updateHelmTag's URL credential injection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Build #10 failed before its sh step even ran: "IllegalArgumentException: named capturing group is missing trailing '}'". Root cause: replaceFirst('http://', "http://\${GIT_USER}:...") — replaceFirst's *replacement* argument is parsed with Java regex-replacement syntax, where ${name} means "substitute named capture group", not literal text. The pattern 'http://' has no named groups, so Java's regex engine choked trying to resolve ${GIT_USER}/${GIT_PASS} as group references. Replaced with a plain string split + concatenation, which has no regex-replacement semantics to collide with, while still keeping \${GIT_USER}/ \${GIT_PASS} literal in the Groovy string so the shell (not Groovy) expands them from the credential-bound env vars at sh-step time. --- src/com/homelab/stages/updateHelmTag.groovy | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/com/homelab/stages/updateHelmTag.groovy b/src/com/homelab/stages/updateHelmTag.groovy index 6c36032..53a8f63 100644 --- a/src/com/homelab/stages/updateHelmTag.groovy +++ b/src/com/homelab/stages/updateHelmTag.groovy @@ -28,7 +28,19 @@ def run(Map config) { withCredentials([usernamePassword(credentialsId: config.gitea_cred ?: 'gitea-ci-credentials', usernameVariable: 'GIT_USER', passwordVariable: 'GIT_PASS')]) { dir('helm-chart-repo') { deleteDir() - def authedUrl = config.helm_repo_url.replaceFirst('http://', "http://\${GIT_USER}:\${GIT_PASS}@") + // Plain split, not replaceFirst — replaceFirst's *replacement* + // argument is also parsed as regex-replacement syntax, where + // ${name} means "substitute named capture group", not literal + // text. Since \${GIT_USER}/\${GIT_PASS} are meant to stay + // literal here (so the *shell* expands them from the + // credential-bound env vars at sh-step time, not Groovy — + // otherwise the secret value would land in a Groovy-processed + // string and defeat withCredentials' masking), that collided + // with a pattern that has no such named group and threw + // `IllegalArgumentException: named capturing group is + // missing trailing '}'`. + def urlParts = config.helm_repo_url.split('://', 2) + def authedUrl = "${urlParts[0]}://\${GIT_USER}:\${GIT_PASS}@${urlParts[1]}" sh """ git clone ${authedUrl} . yq -i '${config.image_tag_yq_path} = "${env.TAG}"' ${valuesFile}