Skip to content

Commit

Permalink
feat: allow publishing reports on infra.ci (#348)
Browse files Browse the repository at this point in the history
* feat(publishReports) allow execution from infra.ci

Signed-off-by: Damien Duportal <damien.duportal@gmail.com>

* fix(publishReports) avoid sh interpolations

Signed-off-by: Damien Duportal <damien.duportal@gmail.com>

* Update vars/publishReports.groovy

Co-authored-by: Tim Jacomb <21194782+timja@users.noreply.github.com>

Co-authored-by: Tim Jacomb <21194782+timja@users.noreply.github.com>
  • Loading branch information
dduportal and timja authored Apr 27, 2022
1 parent be8bf98 commit 799d374
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 53 deletions.
35 changes: 22 additions & 13 deletions test/groovy/PublishReportsStepTests.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import org.junit.Before
import org.junit.Test
import mock.Infra
import static org.junit.Assert.assertEquals
import static org.junit.Assert.assertFalse
import static org.junit.Assert.assertTrue

class PublishReportsStepTests extends BaseTest {
Expand Down Expand Up @@ -40,39 +41,47 @@ class PublishReportsStepTests extends BaseTest {
// then hardcoded credentials is correct
assertTrue(assertMethodCallContainsPattern('string', 'credentialsId=azure-reports-access-key'))
// No execution
assertTrue(helper.callStack.findAll { call -> call.methodName == 'sh' }.isEmpty())
assertFalse(assertMethodCallContainsPattern('sh','az storage blob'))
assertJobStatusSuccess()
}

@Test
void test_with_trusted_and_html_infra() throws Exception {
def script = loadScript(scriptName)
def file = 'foo.html'
// when running with a html filename
script.call(['foo.html'])
script.call([file])
printCallStack()
// then timeout is default and filename manipulations is in place
assertTrue(assertMethodCallContainsPattern('sh', '--timeout=60 --file=foo.html --name=foo.html --content-type="text/html"'))
assertTrue(assertMethodCallContainsPattern('withEnv', 'TIMEOUT=60'))
assertTrue(assertMethodCallContainsPattern('withEnv', "FILENAME=${file}"))
assertTrue(assertMethodCallContainsPattern('withEnv', 'UPLOADFLAGS=--content-type="text/html"'))
assertTrue(assertMethodCallContainsPattern('sh', 'az storage blob upload --account-name=prodjenkinsreports --container=reports --timeout=${TIMEOUT} --file=${FILENAME} --name=${FILENAME} ${UPLOADFLAGS} --overwrite'))
// another filename manipulations is in place
assertTrue(assertMethodCallContainsPattern('sh', '--source . '))
assertTrue(assertMethodCallContainsPattern('sh', '--destination-path / '))
assertTrue(assertMethodCallContainsPattern('sh', '--pattern foo.html '))
assertTrue(assertMethodCallContainsPattern('sh', '--content-type="text/html"'))
assertTrue(assertMethodCallContainsPattern('withEnv', 'SOURCE_DIRNAME=.'))
assertTrue(assertMethodCallContainsPattern('withEnv', 'DESTINATION_PATH=/'))
assertTrue(assertMethodCallContainsPattern('withEnv', 'PATTERN=foo.html'))
assertTrue(assertMethodCallContainsPattern('sh', 'az storage file upload-batch --account-name prodjenkinsreports --destination reports --source ${SOURCE_DIRNAME} --destination-path ${DESTINATION_PATH} --pattern ${PATTERN} ${UPLOADFLAGS}'))
assertJobStatusSuccess()
}

@Test
void test_with_trusted_and_full_path_css_infra() throws Exception {
def script = loadScript(scriptName)
// when running with a css full path filename
script.call(['/bar/foo.css'])
def file = '/bar/foo.css'
script.call([file])
printCallStack()
// then timeout is default and filename manipulations is in place
assertTrue(assertMethodCallContainsPattern('sh', '--timeout=60 --file=/bar/foo.css --name=/bar/foo.css --content-type="text/css'))
assertTrue(assertMethodCallContainsPattern('withEnv', 'TIMEOUT=60'))
assertTrue(assertMethodCallContainsPattern('withEnv', "FILENAME=${file}"))
assertTrue(assertMethodCallContainsPattern('withEnv', 'UPLOADFLAGS=--content-type="text/css"'))
assertTrue(assertMethodCallContainsPattern('sh', 'az storage blob upload --account-name=prodjenkinsreports --container=reports --timeout=${TIMEOUT} --file=${FILENAME} --name=${FILENAME} ${UPLOADFLAGS} --overwrite'))
// another filename manipulations is in place
assertTrue(assertMethodCallContainsPattern('sh', '--source /bar '))
assertTrue(assertMethodCallContainsPattern('sh', '--destination-path /bar '))
assertTrue(assertMethodCallContainsPattern('sh', '--pattern foo.css '))
assertTrue(assertMethodCallContainsPattern('sh', '--content-type="text/css"'))
assertTrue(assertMethodCallContainsPattern('withEnv', 'SOURCE_DIRNAME=/bar'))
assertTrue(assertMethodCallContainsPattern('withEnv', 'DESTINATION_PATH=/bar'))
assertTrue(assertMethodCallContainsPattern('withEnv', 'PATTERN=foo.css'))
assertTrue(assertMethodCallContainsPattern('sh', 'az storage file upload-batch --account-name prodjenkinsreports --destination reports --source ${SOURCE_DIRNAME} --destination-path ${DESTINATION_PATH} --pattern ${PATTERN} ${UPLOADFLAGS}'))
assertJobStatusSuccess()
}
}
83 changes: 43 additions & 40 deletions vars/publishReports.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -8,52 +8,55 @@
def call(List<String> files, Map params = [:]) {
def timeout = params.get('timeout') ?: '60'

if (!infra.isTrusted()) {
if (!infra.isTrusted() && !infra.isInfra()) {
error 'Can only call publishReports from within the trusted.ci environment'
}

withCredentials([string(credentialsId: 'azure-reports-access-key', variable: 'AZURE_STORAGE_KEY')]) {
docker.image('mcr.microsoft.com/azure-cli:2.0.41').inside {
for (int i = 0; i < files.size(); ++i) {
String filename = files[i]
withEnv(['HOME=/tmp']) {
String uploadFlags = ''
switch (filename) {
case ~/(?i).*\.html/:
uploadFlags = '--content-type="text/html"'
break
case ~/(?i).*\.css/:
uploadFlags = '--content-type="text/css"'
break
case ~/(?i).*\.json/:
uploadFlags = '--content-type="application/json"'
break
case ~/(?i).*\.js/:
uploadFlags = '--content-type="application/javascript"'
break
case ~/(?i).*\.gif/:
uploadFlags = '--content-type="image/gif"'
break
case ~/(?i).*\.png/:
uploadFlags = '--content-type="image/png"'
break
}
// Blob container can be removed once files are uploaded on the azure file storage
sh "az storage blob upload --account-name=prodjenkinsreports --container=reports --timeout=${timeout} --file=${filename} --name=${filename} ${uploadFlags}"
withCredentials([string(credentialsId: 'azure-reports-access-key', variable: 'AZURE_STORAGE_KEY'),]) {
// Sanity Check to check that `az` is installed, in the PATH, and in a decent version
sh 'az version'

// `az storage file upload` doesn't support file uploaded in a remote directory that doesn't exist but upload-batch yes. Unfortunatly the cli syntax is a bit different and require filename and directory name to be set differently.
for(int i = 0; i < files.size(); ++i) {
String filename = files[i]
withEnv(['HOME=/tmp']) {
String uploadFlags = ''
switch (filename) {
case ~/(?i).*\.html/:
uploadFlags = '--content-type="text/html"'
break
case ~/(?i).*\.css/:
uploadFlags = '--content-type="text/css"'
break
case ~/(?i).*\.json/:
uploadFlags = '--content-type="application/json"'
break
case ~/(?i).*\.js/:
uploadFlags = '--content-type="application/javascript"'
break
case ~/(?i).*\.gif/:
uploadFlags = '--content-type="image/gif"'
break
case ~/(?i).*\.png/:
uploadFlags = '--content-type="image/png"'
break
}
def directory = filename.split("/")
def basename = directory[directory.size() - 1]
def dirname = Arrays.copyOfRange(directory, 0, directory.size()-1 ).join("/")

def directory = filename.split("/")
def basename = directory[directory.size() - 1]
def dirname = Arrays.copyOfRange(directory, 0, directory.size() - 1).join("/")
withEnv([
"TIMEOUT=${timeout}",
"FILENAME=${filename}",
"UPLOADFLAGS=${uploadFlags}",
"SOURCE_DIRNAME=${dirname ?: '.'}",
"DESTINATION_PATH=${dirname ?: '/'}",
"PATTERN=${ basename ?: '*' }",
]) {
// Blob container can be removed once files are uploaded on the azure file storage
sh 'az storage blob upload --account-name=prodjenkinsreports --container=reports --timeout=${TIMEOUT} --file=${FILENAME} --name=${FILENAME} ${UPLOADFLAGS} --overwrite'

sh "az storage file upload-batch \
--account-name prodjenkinsreports \
--destination reports \
--source ${dirname ?: '.'} \
--destination-path ${dirname ?: '/'} \
--pattern ${ basename ?: '*' } \
${uploadFlags}"
// `az storage file upload` doesn't support file uploaded in a remote directory that doesn't exist but upload-batch yes. Unfortunately the cli syntax is a bit different and requires filename and directory name to be set differently.
sh 'az storage file upload-batch --account-name prodjenkinsreports --destination reports --source ${SOURCE_DIRNAME} --destination-path ${DESTINATION_PATH} --pattern ${PATTERN} ${UPLOADFLAGS}'
}
}
}
Expand Down

0 comments on commit 799d374

Please sign in to comment.