Search code examples
githubjenkins-pipelinegithub-actions

How to reuse script from Jenkins in GitHub Actions


I am migrating my code from Jenkins to GitHub Actions. My code in Jenkins is already in Production.

What does my pipeline do?

  1. Reads Config.json file that is on GitHub.

  2. In the Script, it initializes all variables, arrays, objects based on the values from Config.json.

  3. Fires series of cURL commands based on the conditions.

What I intend to do?

Instead of reinventing the wheel for the GitHub Actions, I want to reuse the script {} code from Jenkins in the GitHub Actions by executing script.groovy from migrateToGitHubActions.yaml.

script.groovy has the exact code of script {} from Jenkins pipeline.

From my Jenkins:

    pipeline{
    agent any
    
    stages{
        stage('Start'){
            steps{
                sh 'ls'
            }
        }
        stage('Clone Repo'){
            steps{
                git credentialsId: 'githubapp', url: 'https://github.com/configurations.git', branch: "${env}"
            }
        }
        stage('Configure'){
            steps{
                script{
                    sh 'cat Config.json'
                    def configs = readJSON(file: 'Config.json')
                    def totalBundles = configs.bundles.size()
                    println "Total Array Size =" + totalBundles
                    def productName = new Object[totalBundles]
                    ....
                }
            }
        }
    }
}

Config.json:

    {
    "bundles":
    [
        {
            "product":{
                "name" : "Product1",
                "proxies" : ["'Proxy1', 'Proxy2'"],
                "attributes" : [{"name":"Test1","value":"abc"}]
            },
            "app":{
                "name":"App1",
                "attributes": [{"name": "attribute1","value": "null"}],
                "developer": "[email protected]"
            }
        },
        {
            "product":{
                "name" : "Product2",
                "proxies" : ["'Proxy1', 'Proxy2'"],
                "attributes" : [{"name":"Test2","value":"xyz"}]
            },
            "app":{
                "name":"App1",
                "attributes": [{"name": "attribute2","value": "null"}],
                "developer": "[email protected]"
            }
        }
    ]
}

My GitHub Actions migrateToGitHubActions.yaml:

name: Config

on:
    workflow_dispatch:

jobs:
  build:
    runs-on: [abc]

    steps:
      - name: Checkout repository
        uses: actions/checkout@v3

      - name: List files (Start)
        run: |
            ls

      - name: Set up JDK 11
        uses: actions/setup-java@v3
        with:
          java-version: '11'
          distribution: 'adopt'
      - name: Install Groovy
        run: sudo apt-get update && sudo apt-get install -y groovy
      - name: Run Groovy script
        run: groovy script.groovy

script.groovy:

#!/usr/bin/env groovy

import groovy.json.JsonSlurper

println "*** Before Read ***"

// Reading JSON from file
def jsonFile = new File('AppsProductsConfig.json')
def configs = new JsonSlurper().parseText(jsonFile.text)

println "configs = $configs"
println "*** After Read ***"

def totalBundles = configs.bundles.size()
println "Total Array Size = $totalBundles"

// Initialization of arrays
def productName = new String[totalBundles]
def developer = new String[totalBundles]
def firstName = new String[totalBundles]
def lastName = new String[totalBundles]
def userName = new String[totalBundles]
def approvalType = new String[totalBundles]
def productAttributeName = new String[totalBundles]
def productAttributeValue = new String[totalBundles]
def displayName = new String[totalBundles]
def environments = new String[totalBundles]
def proxies = new Object[totalBundles][]
def appName = new String[totalBundles]
def appAttributeName = new String[totalBundles]
def appAttributeValue = new String[totalBundles]
def host = "example.com"
def USER = System.getenv('USER')

println "*** After Initialisation Read ***"

// Extract and Store Values
println "**** Read Variables ***"
for (int i = 0; i < totalBundles; i++) {
    def bundle = configs.bundles[i]

    // Products
    productName[i] = bundle.product.name
    approvalType[i] = "auto"
    productAttributeName[i] = bundle.product.attributes[0].name
    productAttributeValue[i] = bundle.product.attributes[0].value
    displayName[i] = bundle.product.name
    environments[i] = System.getenv('env') // Assuming 'env' is an environment variable
    proxies[i] = bundle.product.proxies

    // Developer
    developer[i] = bundle.app.developer
    firstName[i] = developer[i].takeWhile { it != '.' }
    def start = developer[i].indexOf(".") + 1
    def end = developer[i].indexOf("@")
    lastName[i] = developer[i].substring(start, end).replaceAll(/[^a-zA-Z]/, "")
    userName[i] = developer[i].takeWhile { it != '@' }

    // Apps
    appName[i] = bundle.app.name
    appAttributeName[i] = bundle.app.attributes[0].name
    appAttributeValue[i] = bundle.app.attributes[0].value

    println "*** Print Apps & Products ***"
    println "Product Name = $productName[i]"
    println "App Name = $appName[i]"
    println "Approval Type = $approvalType[i]"
    println "Product Attributes Name = $productAttributeName[i]"
    println "Product Attributes Value = $productAttributeValue[i]"
    println "Display Name = $displayName[i]"
    println "Environments = $environments[i]"
    println "Proxies = ${proxies[i]}"
    println "Developer = $developer[i]"
    println "App Attributes Name = $appAttributeName[i]"
    println "App Attributes Value = $appAttributeValue[i]"
}

for (int i = 0; i < totalBundles; i++) {
    def productBody = """{
        "approvalType": "${approvalType[i]}",
        "attributes": [{"name": "${productAttributeName[i]}", "value": "${productAttributeValue[i]}"}],
        "displayName": "${displayName[i]}",
        "environments": ["${environments[i]}"],
        "name": "${productName[i]}",
        "proxies": ${proxies[i]}
    }"""
    println "Product Body = $productBody"

    def developerBody = """{
        "email": "${developer[i]}",
        "firstName": "${firstName[i]}",
        "lastName": "${lastName[i]}",
        "userName": "${userName[i]}"
    }"""
    println "Developer Body = $developerBody"

    def appBody = """{
        "name": "${appName[i]}",
        "apiProducts": ["${productName[i]}"],
        "attributes": [{"name": "${appAttributeName[i]}", "value": "${appAttributeValue[i]}"}],
        "keyExpiresIn": "31536000000"
    }"""
    println "App Body = $appBody"

    // Products
    def createProductResponse = sh(script: "curl -k -s -w '\\n%{response_code}' --header 'Authorization:Basic $USER' --header 'Content-Type: application/json' --data-binary '$productBody' --request POST https://${host}/apiproducts", returnStdout: true).trim()
    println "$createProductResponse"

    if (createProductResponse.contains('201')) {
        println "*** Product ${productName[i]} created successfully ***"
    } else if (createProductResponse.contains('409')) {
        def updateProductResponse = sh(script: "curl -k -s -w '\\n%{response_code}' --header 'Authorization:Basic $USER' --header 'Content-Type: application/json' --data-binary '$productBody' --request PUT https://${host}/apiproducts/${productName[i]}", returnStdout: true).trim()
        println "$updateProductResponse"
        if (updateProductResponse.contains('200')) {
            println "*** Product ${productName[i]} updated successfully ***"
        } else {
            error "Failed to update ${productName[i]} with $updateProductResponse"
        }
    } else {
        error "Failed to create ${productName[i]} with $createProductResponse"
    }

    // Developers
    def createDeveloperResponse = sh(script: "curl -k -s -w '\\n%{response_code}' --header 'Authorization:Basic $USER' --header 'Content-Type: application/json' --data-binary '$developerBody' --request POST https://${host}/developers", returnStdout: true).trim()
    println "$createDeveloperResponse"

    if (createDeveloperResponse.contains('201')) {
        println "*** Developer ${developer[i]} created successfully ***"
    } else if (createDeveloperResponse.contains('409')) {
        println "*** Developer already exists: $createDeveloperResponse ***"
    } else {
        error "Failed to create ${developer[i]} with $createDeveloperResponse"
    }

    // Apps
    def createAppResponse = sh(script: "curl -k -s -w '\\n%{response_code}' --header 'Authorization:Basic $USER' --header 'Content-Type: application/json' --data-binary '$appBody' --request POST https://${host}/developers/${developer[i]}/apps", returnStdout: true).trim()
    println "$createAppResponse"

    if (createAppResponse.contains('201')) {
        println "*** App ${appName[i]} created successfully ***"
    } else if (createAppResponse.contains('409')) {
        def updateAppBody = """{
            "attributes": [{"name": "${appAttributeName[i]}", "value": "${appAttributeValue[i]}"}]
        }"""
        println "Updated App Body = $updateAppBody"

        def updateAppResponse = sh(script: "curl -k -s -w '\\n%{response_code}' --header 'Authorization:Basic $USER' --header 'Content-Type: application/json' --data-binary '$updateAppBody' --request PUT https://${host}/developers/${developer[i]}/apps/${appName[i]}", returnStdout: true).trim()
        println "$updateAppResponse"
        if (updateAppResponse.contains('200')) {
            println "*** App ${appName[i]} updated successfully ***"
        } else {
            error "Failed to update ${appName[i]} with $updateAppResponse"
        }
    } else {
        error "Failed to create ${appName[i]} with $createAppResponse"
    }
}

Folder Structure:

enter image description here

I run into errors at most of the lines while running migrateToGitHubActions.yaml

Do GitHub Actions have fewer scripting capabilities than Jenkins? What kind of script is expected in Actions?

Error:

WARNING: An illegal reflective access operation has occurred WARNING: Illegal reflective access by org.codehaus.groovy.reflection.CachedClass (file:/usr/share/groovy/lib/groovy-2.4.17.jar) to method java.lang.Object.finalize() WARNING: Please consider reporting this to the maintainers of org.codehaus.groovy.reflection.CachedClass WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations WARNING: All illegal access operations will be denied in a future release Caught: groovy.lang.MissingPropertyException: No such property: json for class: java.io.File groovy.lang.MissingPropertyException: No such property: json for class: java.io.File at script.run(script.groovy:3) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) Error: Process completed with exit code 1.


Solution

  • I've tested your workflow configuration and the issue you are facing seems related to the groovy script.

    After a few researches (and Chat GPT usage) I identified that some command lines you are using with Jenkins aren't available on Github Actions directly.

    Example: the sh commands is only available on Jenkins, you should replace this command line by execute in your groovy script, this operation should then work when running the script in the workflow.

    Moreover, I asked chat GPT to correct the first version of the groovy script you shared (I'm not familiar with groovy) and got the workflow to work here by using this workflow file - updating some actions versions - and this updated groovy script.


    Note: If you face more issues with the groovy script, I suggest asking another question on StackOverflow with the groovy tag, for the groovy community to give a more detailed answer.