Kontakt & Service
Jetzt Beratung vereinbaren

In wenigen Schritten einen Beratungs­termin mit unseren Experten buchen.

Anwender Helpdesk

Hilfestellung bei Problemen in Ihren SAP-Systemen.

Schulungen

Unser Schulungsangebot. Jetzt informieren!

Webinare

Unser Webinarangebot. Jetzt informieren!

SAP CI/CD infrastrukturunabhängig

News & Wissen Infrastrukturunabhängige Nutzung von Continuous Integration & Deployment (CI/CD)

Die infrastrukturellen Voraussetzungen sind bei Kunden oft grundverschieden. Damit ist nicht nur die Verfügbarkeit der individuell favorisierten Entwicklungsumgebung gemeint. Manchmal stellt sogar der Zugang zum Internet eine Hürde dar. Oder das Nichtvorhandensein eines SAP Cloud-Accounts inklusive WebIDE. Hinzu kommen Terminal-Systeme, auf denen Programminstallationen einen erhöhten Aufwand mit sich bringen.

Den Luxus einer stets einheitlichen bzw. gewohnten Entwicklungsumgebung, wie es bei der ABAP Workbench der Fall ist, hat man in der SAPUI5-Welt nicht immer. Aus diesem Grund war es recht naheliegend, eine Möglichkeit zu schaffen, die so unabhängig wie nur irgendwie möglich von der bestehenden Infrastruktur agieren kann.

Das Grunt-Skript

Schnell fiel das Augenmerk auf Grunt. Ein „Task Runner“ zur Automatisierung von geskripteten Abläufen. Die SAP bietet hier bereits, innerhalb des Node package managers (npm), fertige Module an.

Ist npm verfügbar, bestehen aufgrund des Grunt-Skripts keine Restriktionen bei der Wahl der Entwicklungsumgebung, da alle Abläufe bezüglich Continuous Integration (CI) & Continuous Deployment (CD) außerhalb stattfinden.

Aufbau des Gruntfile-Grundgerüstes

module.exports = function(grunt) {

    'use strict';

    grunt.loadNpmTasks('@sap/grunt-sapui5-bestpractice-build');

    // These plugins provide necessary tasks.
    grunt.loadNpmTasks('grunt-nwabap-ui5uploader');
    grunt.loadNpmTasks('grunt-contrib-connect');
    grunt.loadNpmTasks('grunt-contrib-clean');
    grunt.loadNpmTasks('grunt-contrib-copy');
    grunt.loadNpmTasks('grunt-openui5');
    grunt.loadNpmTasks('grunt-git');
    grunt.loadNpmTasks('grunt-contrib-watch');

    grunt.config.merge({

        //Some own task configurations

    });

    grunt.registerTask('default', [
        'clean',
        'copy:all',
        'lint',
        'serve:dist'

    ]);

};

Das Grundgerüst besteht im minimalen Fall aus ein paar anderen Grunt-Tasks, die hier geladen und in einer festen Ausführungsreihenfolge zu neuen Tasks zusammengefasst werden. Dabei lassen sich auch selbst Tasks konfigurieren. Wichtig ist hier die Methode „merge“ zu benutzen, um die Konfigurationen der bereits geladenen Tasks wie „grunt-git“ nicht zu überschreiben.

Im oberen Teil des Skripts befinden sich einige Variablendefinitionen, um die benötigten Daten für die Aufgaben an einem Ort gesammelt aufzubewahren.

        localServer: {
            port: 8081

        },
        deploymentConfig: {
            server: 'http(s)://die.domain:Port',
            package: 'Paket',
            bspcontainer: 'bspName',
            bspcontainer_text: 'bspBeschreibung‘,
            transportno: 'transportauftrag'
        },

        dir: {
            webapp: 'webapp',
            dist: 'dist',
            bower_components: './node_modules/bower_components',
            resources: [
                '<%= dir.bower_components %>/openui5-sap.ui.core/resources',
                '<%= dir.bower_components %>/openui5-sap.m/resources',
                '<%= dir.bower_components %>/openui5-sap.f/resources',
                '<%= dir.bower_components %>/openui5-sap.uxap/resources',
                '<%= dir.bower_components %>/openui5-sap.uxap.sample/resources',
                '<%= dir.bower_components %>/openui5-sap.ui.layout/resources',
                '<%= dir.bower_components %>/openui5-sap.ui.table/resources',
                '<%= dir.bower_components %>/openui5-sap.ui.unified/resources',
                '<%= dir.bower_components %>/'+
                'openui5-themelib_sap_belize/resources'
            ]
        },

Auf die einzelnen Variablen ist mit Hilfe folgender Syntax zuzugreifen:
„<%= dir.dist %>" greift auf die Variable „dir“ und dessen Kind „dist“ zu. Es wird der String „dist“ gelesen.

Dabei sind Möglichkeiten des Skripts sehr umfangreich:

  • Start eines lokalen, minimalistischen Webservers inklusive „Watch-Task“

Mittels eines Konsolenbefehls kann der aktuelle JavaScript-Code im Browser dargestellt und auf das gewünschte Verhalten hin überprüft werden. Kommt es zu einer Änderung des Codes, wird diese vollautomatisch durch die „Watch-Task“ erkannt und das erneute Laden genügt, um die Änderungen sichtbar zu machen.

„Webserver"-Konfiguration über:

    connect: {
            options: {
                port: '<%= localServer.port %>',
                hostname: '*'
            },
            src: {},
            dist: {}
        },

        openui5_connect: {
            options: {
                resources: '<%= dir.resources %>'
            },
            src: {
                options: {
                    appresources: '<%= dir.webapp %>'
                }
            },
            dist: {
                options: {
                    appresources: '<%= dir.dist %>'
                }
            }
        },

„grunt-openui5“ und „grunt-contrib-connect“ stellen zusammen den Webserver. Die Konfiguration des Ports erfolgt über das Objekt „connect“, bei den SAPUI5-applikationsspezifischen Eigenschaften über „openui5_connect“. Gestartet wird letztendlich „openui5_connect“ in der Ausprägung „dist“ mit der zusätzlichen Option „keepalive“, um den Server auch nach Durchlaufen des Skripts aktiv zu halten. Zusätzlichen werden hier lokale Libraries, die mit „Bower“ verfügbar gemacht worden sind, verwendet.

grunt.loadNpmTasks('grunt-contrib-connect');
grunt.loadNpmTasks('grunt-contrib-clean');
grunt.loadNpmTasks('grunt-contrib-copy');
grunt.loadNpmTasks('grunt-openui5');

„Watch“-Konfiguration über:

        watch: {
            files: ["webapp/**"],
            tasks: ["clean", "copy:all"]
        },

Wenn „watch“ ausgeführt wird, werden für den Server die Daten aus dem Ordner „webapp“ verwendet und zusätzlich die Tasks „clean“ und „Copy in der Ausprägung ‚all‘" ausgeführt.

  • Code-Komprimierung wie „Minifying“ oder „Uglifying“

Um die Übertragungsgeschwindigkeiten und Ladezeiten immer größer werdender Web-Projekte zu beschleunigen, wird die Anzahl der einzelnen Dateien sowie deren Inhalte auf ein Minimum reduziert. Das „Uglifying“ bietet zudem eine effektive Methode zum Schutz des geistigen Eigentums.

Konfiguration über:

        uglify:{
            uglifyDist: {
                files: [
                    {
                        expand: true,
                        src: '**/*.js',
                        dest: '<%= dir.dist %>',
                        cwd: '<%= dir.appFolder %>'
                    }]
            }
        },
    });

Bei dieser Task werden sowohl „Uglifying“ als auch „Minifying“ durchgeführt und alle JavaScript-Dateien verwendet. Quelle ist der Applikationsordner; die bearbeiteten Dateien landen in dem Ordner „Dist“.

  • Überprüfung des Programmcodes auf Fehler mittels Lint

Die Regeln zur Codeüberprüfung können selbst festgelegt werden und lassen sich somit ganz einfach den vor Ort herrschenden Programmierrichtlinien anpassen.

Es wird die vorgegebene Konfiguration der SAP für die Lint-Task verwendet.

  • Automatische „Commits“ auf Versionsverwaltungssystemen wie Git

Innerhalb des Entwicklungsprozesses sowie spätestens beim Deployment einer Applikation kann ein lokales und / oder verteiltes Repository automatisch aktualisiert werden, ohne diesen Prozess in seinen einzelnen Schritten manuell durchführen zu müssen.

Konfiguration über:

        gitadd: {
            task: {
                options: {
                    all: true
                }
            }
        },

        gitcommit: {
            task: {
                options: {
                    message: sMessage
                }
            }
        },

        gitpush: {
          task: {
              options: {
                  branch: 'develop'
              }
          }
        },

Hier werden drei Tasks konfiguriert: „gitadd“ fügt alle neuen Dateien dem nächsten Commit hinzu. „gitcommit“ führt den Commit durch und verwendet dabei die übergebene Beschreibung. Der Abgleich mit dem remote Repository findet über „gitpush“ statt. Dabei wird der Branch „develop“ genutzt.

  • Deployment auf definite ABAP-Ressourcen

Ist eine Applikation bereit für ihr Deployment, können alle bereits beschriebenen Schritte sowie das Deployment selbst mit nur einem Konsolenbefehl automatisiert werden.

Konfiguration über:

nwabap_ui5uploader: {
            options: {
                conn: {
                    server: '<%= deploymentConfig.server %>'
                },
                auth: {
                    user: sUser,
                    pwd: sPwd,
                }
            },
            upload_build: {
                options: {
                    ui5: {
                        package: '<%= deploymentConfig.package %>',
                        bspcontainer: '<%= deploymentConfig.bspcontainer %>',
                        bspcontainer_text:
                          '<%= deploymentConfig.bspcontainer_text %>',
                        transportno: '<%= deploymentConfig.transportno %>'
                    },
                    resources: {
                        // the source which will be transported
                        cwd: '<%= dir.dist %>',
                        src: '**/*.*'
                    }
                }
            }
        },

Die Applikation wird über den ADT-Knoten in das SAP-System deployed. Somit muss das „Appindex“ anschließend nicht neu aufgebaut werden. Dazu wird bei Aufruf der Task der Benutzername sowie auch das Passwort des SAP-Benutzers mitgegeben. Alle anderen Angaben sind bereits im oberen Teil durch Konfigurationsvariablen festgelegt worden.

  • Eigene Tasks

Zwei weitere Aufgaben sowie die „default“-Aufgabe sind in dem Script definiert worden:

grunt.registerTask('deploy',[
        'clean',
        'lint',
        'build',
        'uglify:uglifyDist',
        'cssmin',
        'gitadd',
        'gitcommit',
        'gitpush',
        'nwabap_ui5uploader'
    ]);

    grunt.registerTask('clearDeploy', [
        'clean',
        'copy:all',
        'lint',
        'createsCachebusterInfoJson',
        'nwabap_ui5uploader'
    ]),

    grunt.registerTask('default', [
        'clean',
        'copy:all',
        'lint',
        'serve:dist'
    ]);

Bei allen Aufgaben handelt es sich um einen Ablaufplan von weiteren Aufgaben, die ihrerseits wiederum aus mehreren Aufgaben bestehen können. Am Beispiel der Standardaufgabe werden nacheinander „clean“, „copy“ mit der Ausprägung „all“, „lint“ und schließlich „serve“ mit der Ausprägung „dist“ ausgeführt.

Die „default“-Aufgabe wird genutzt, wenn bei Aufruf des Skripts keine weitere Option mitgeliefert wird.

Durch diese Lösung sind diverse Anpassungen sowie Erweiterungen in den einzelnen Schritten möglich und dem Entwickler ist damit maximale Unabhängigkeit von der verfügbaren Infrastruktur gegeben.

Start des Skripts über die Kommandozeile

Aufrufbeispiel der Task „deploy“:

grunt deploy --message=„commitMessage" --user="deinUser" --pwd="deinPW„

Hier werden zusätzlich zum Namen der auszuführenden Task noch drei Variablen übergeben, die in dem Skript wie folgt ausgelesen werden:

var sUser = grunt.option('user');
    var sPwd = grunt.option('pwd');
    var sMessage = !grunt.option('message')
                       ? "automaticCommit"
                       : grunt.option('message');

Fazit

Es handelt sich um eine einfache Möglichkeit, die Einarbeitungsphase zu kürzen und dadurch die Entwicklungskosten zu senken.

Dies wird unter anderem durch folgende Punkte erreicht:

  • Analyse der Systemlandschaft stark reduziert
  • Keine Einarbeitung in unbekannte Werkzeuge nötig
  • Lizenzkosten können minimiert werden
  • Komfort bei der Entwicklung (Code-Analyse, Versionsverwaltung, Live-Vorschau und weitere)
Newsletter Setzen Sie auf fundiertes Wissen aus allen Bereichen unserer Branche. Regelmäßig und stets aktuell.
Beratende Person
Kontakt Haben Sie Fragen oder wünschen weitere Informationen? Unsere Experten beraten Sie gerne.