Organisation meiner OXID6 CE Themes & Module

(nach dem Upgrade von OXID4)

lokale Installation

Seit OXID4 (v4.9.7) nutze ich Git als Versionsverwaltungssystem während der Entwicklung. Jedes OXID4-Kundenprojekt hatte sein eigenes Repository. Darin befanden sich die OXID-Core, alle zum Projekt gehörenden Module und das Projekt-Childtheme. Kam ein Core- oder Modul-Update wurden die neuen Dateien in das lokale Repository eingespielt und die Anpassungen zeilenweise mit einem Compare-Tool verglichen. So konnte ich alle, für die Module oder Child-Themes relevanten Anpassungen erkennen und gleichermaßen nachziehen. Lief lokal alles, wurden die betroffenen Dateien per FTP erst im Stage-System und nach erneutem Test dann ins Live-System übertragen. Schon bei zehn, sich im Support befindlichen Projekten, war der Deployment-Aufwand bei Core-Updates oder Updates an zentralen Modulen immens.

Lokal verwende ich für Projekte, an denen ich allein arbeite ein WAMP-System auf Basis von wampserver.com. Auch wenn es mit OXID6 interessante Komplett-Pakete als Virtual Machines oder Docker-Container gibt, bin ich bei WAMP geblieben. So kann ich weiterhin meine „alten“ OXID4 Projekte pflegen und parallel die neuen OXID6 Projekte starten. Ich muss lediglich das PHP von 5.6 auf 7.2 wechseln und zurück. Das geht dank dem WindowsTray-Menü von WAMP mit zwei Klicks. Alle Kundenprojekte liegen bei mir in separaten Ordnern die per Alias im WAMP eingebunden sind. Composer ist via Windows-Installer installiert und greift auf die PHP7.2 Version des WAMP-PHPs zurück.
Für den Zugriff auf GIT nutze ich das aktuelle Git. Als IDE phpStorm, aber am Ende für die tagtägliche Arbeit auch immer noch gerne UltraEdit und UltraCompare. Da ich kein command-line-Fetischist bin und die GIT-Integration im phpStorm nicht ganz so mein Ding ist, nutze ich als GIT-Gui GitExtensions.

Im Team ist die Arbeit mit WAMP nicht zu empfehlen. Hier hat sich ein stufiges System (lokal arbeiten, Deployment auf Entwicklungs-Server mit eigener DB-Instanz, Stage-Deployment mit gemeinsamer DB-Instanz und Zugriffs- und Freigabemöglichkeit durch Kunden und abschließendem Live-Deployment bewert. Hier gibt es viele Möglichkeiten der Automatisierung, die aber über Ziel dieses Beitrages hinausgehen.

Mit OXID6 erfolgte ein organisatorischer Umbruch seitens OXID eSales. Composer spielt nun eine entscheidende Rolle beim Deployment. Ich kannte Composer bereits durch die Arbeit mit Typo3 und wusste um die Rolle von composer.json-Files in den Repositories. Ich schaute mir also zuerst an, wie OXID diese Dateien bei seinen Projekten, Modulen und Themes zusammenstellte.

Nach der ersten OXID6-Test-Installation via Composer nach offizieller Anleitung, war ich vom Inhalt, der erstellten composer.json Datei im Root-Verzeichnis überrascht. Es gab nur ein require hin zur „oxideshop_metapackage_ce„. In der dortigen composer.json befanden sich dann alle weiteren Requires. Auch die Aufteilung in das vendor und das source Verzeichnis fand ich im ersten Moment ungewöhnlich. Bei näherer Betrachtung aber durchaus sinnvoll. Das vendor-Verzeichnis wird von composer kontrolliert. Im source-Verzeichnis könnten theoretisch, neben den durch composer bereitgestellten Sourcen weitere Scripte hinterlegt werden. Sicherlich könnte man noch mehr Dateien, die unter source und vendor liegen im vendor belassen, möglicherweise besteht aber so die Möglichkeit der besseren Abwärtskompatibilität zu OXID4.

In der metapackage-composer.json fiel auf, das alle Requires keine Angaben zu den Quell-Repositories hatten. Somit war klar, dass es zu jedem Require ein übergreifendes, frei zugängliches Composer-Repository auf packagist.org gibt. Interessant für mich war, dass die Themes und die Module eigene Repositories hatten. Das war sicher schon immer so, nur in dem Moment wurde mir der Sinn erst klar. Die Frage war nun, wie bekomme ich jetzt meine Module über diesen offiziellen Weg in meine OXID-Installation rein? Ich hoste meine Projekte schon immer bei bitbucket.org. Ich wusste, das ich einige kundenspezifische Module nicht als öffentliche Repositories bereitstellen kann. Andere Module waren förmlich prädestiniert dafür, auch öffentlich zugänglich zu werden. Ich legte also in bitbucket.org die ersten leeren OXID6-Modul-, -Projekt- und Child-Theme-Repositories an. Hier unterschied ich zwischen private und public Zugriff. Bei packagist.org kam ein Account hinzu. Die public-Bitbucket-Repositories bekamen auch ein Repository bei packagist.org. In Bitbucket kann man über Web-Hooks einen Push zu packagist.org ausgelöst werden. Die public-Repositories konnte ich nun analog zu den OXID-eSales-Repositories in meine composer.json, für meine erste eigene Projekt-Installation hinzufügen. In die eigene Root-composer.json habe ich nur das oxid-esales/oxideshop-metapackage-ce-repository gelassen und mit meinen public Repositories und dem alten OXID-PDF-Invoice-Module ergänzt. In der oxid-esales/oxideshop-metapackage-ce sind aber auch Repositories enthalten, die ich nicht brauchte. Dazu gehören überflüssige Zahlungsmodule, Demo-Daten und eines der beiden Standard-Themes. Diese Repositories konnte ich durch die Nutzung von replace unterdrücken. Hier das Ergebnis:

{
	"name": "therealworld/demo-oxid6",
	"type": "project",
	"description": "This file should be used as an OXID eShop project root composer.json file. Entries provided here intended to be examples and could be changed to your specific needs.",
	"license": [
		"GPL-3.0-only",
		"proprietary"
	],
	"minimum-stability": "stable",
	"repositories": [
		{"type": "vcs", "url": "git@bitbucket.org:therealworld/theme-1.git"},
		{"type": "vcs", "url": "git@bitbucket.org:therealworld/module-1.git"},
		{"type": "vcs", "url": "git@bitbucket.org:therealworld/module-2.git"},
		{"type": "vcs", "url": "git@bitbucket.org:therealworld/module-3.git"},
		{"type": "vcs", "url": "git@bitbucket.org:therealworld/module-4.git"}
	],
	"require": {
		"oxid-esales/oxideshop-metapackage-ce": "v6.1.4",
		"oxid-projects/pdf-invoice-module": "v2.1.*",
		"therealworld/scheduler-module": "v1.14.*",
		"therealworld/staticcache-module": "v1.1.*",
		"therealworld/tools-module": "v1.7.*",
		"therealworld/module-1": "v1.1.*",
		"therealworld/module-2": "v1.1.*",
		"therealworld/module-3": "v1.1.*",
		"therealworld/module-4": "v1.1.*",
		"therealworld/theme-1": "v1.1.*"
	},
	"replace": {
		"oxid-esales/flow-theme": "*",
    	"oxid-esales/oxideshop-demodata-installer": "*",
		"oxid-esales/oxideshop-demodata-ce": "*",
		"oxid-esales/paymorrow-module": "*",
		"ddoe/wysiwyg-editor-module": "*",
		"payone-gmbh/oxid-6": "*",
		"topconcepts/oxid-klarna-6": "*",
		"amzn/amazon-pay-sdk-php": "*",
		"bestit/amazonpay4oxid": "*"
	},
	"config": {
		"preferred-install": {
			"*": "dist"
		}
	}
}

Für die privaten Repositories, auf die nicht frei zugegriffen werden kann, gibt es folgende Lösung: Mit PuTTYgen habe ich ein ssh-RSA-Schlüsselpaar erstellt. Der öffentliche Schlüssel ist in allen Bitbucket-Repositories hinterlegt. Wenn ich nun lokal mit GitExtensions auf die Repositories zugreife, läuft parallel der pAgent von PuTTY und sorgt so für die Bereitstellung der Schlüssel und damit für den Zugriff auf die privaten Repositories.
Auch mein lokales composer, nutzt den im Hintergrund laufenden pAgent für den Zugriff auf die online-Repositories. Die lokalen composer updates mit meiner neuen composer.json liefen erfolgreich. Es wurde der source– und vendor-Ordner sowie die composer.lock parallel zur composer.json erstellt. In mein Projekt-Repository sind so am Ende nur vier Dateien. Der Rest wird per .gitignore ignoriert (ein großer Fortschritt gegenüber OXID4):

  • composer.json
  • composer.lock
  • readme.md
  • .gitignore

Online Installation

Online geht es ähnlich. Ich verbinde mich mit SSH (PuTTY) auf dem Server. Im PuTTY-Serverprofil ist das Agent-Forwarding aktiviert. Läuft also pAgent während meiner PuTTY-Sitzung, so gilt für meine Zugriffe vom Server auf die BitBucket-Repositories mein lokaler ssh-RSA-Schlüssel. Somit reicht es aus, in die composer.json im Bereich repositories alle Repositories zu ergänzen, die ich nicht per packagist.org zugänglich sind.

Die composer.json und composer.lock aus meinem Testprojekt habe ich der Einfachheit per FTP auf den Server geladen. Da ja alle Abhängigkeiten bereits lokal in die composer.lock geschrieben wurden, reichte es auf dem Server aus, mittels composer install den Shop online zu installieren.

Projekte, Module und Themes während der Arbeit

Mit phpStorm

Wer mit phpStorm arbeitet, kann sich ein Projekt direkt in der Shop-Root anlegen. PhpStorm scannt und indiziert gleich die komplette Installation. Die Repositories aus dem Vendor-Verzeichnis, die nicht über packagist.org installiert wurden, sondern direkt aus einem GIT-Repository kommen, werden sogar so eingebunden, das man gleich mit diesen Repositories arbeiten kann.
Bei den Repositories via packagist.org kann man noch die original GIT-Remotes ergänzen. Dann können auch die Repositories gleich direkt mit in PhpStorm bearbeitet werden.

Ohne phpStorm

Wer kein phpStorm hat und auch nicht direkt in dem vendor-Verzeichnis seine Module arbeiten möchte, kann einen anderen Weg gehen. Angenommen, die Module und Themes liegen lokal und zentral an einer anderen Stelle ausserhalb der OXID-Installation und man möchte gern, das die Änderungen an den Modulen gleich in die Modulordner der Developer-Installation des Shops kopiert werden sollen, so kann man dafür Werkzeuge von Node.js nutzen. Eine lokale Node.js-Installation ist für die Child-Theme-Arbeit unumgänglich. Dazu in einem späteren Artikel mehr. Mit Node.js erhält man den Paketmanager npm und damit Zugriff auf tausende JS-Script-Repositories für alle nur denkbaren Situationen. Dort gibt es CPX, ein CLI-Tool das nichts anderes macht, als Ordner zu überwachen und bei Änderungen diese an einer gewünschten anderen Stelle ebenfalls vorzunehmen. Mit dem weiteren Tool concurrently können die CPX-Prozesse auch parallel ausgeführt werden.
Mit einem kleinen command-Script lasse ich nun alle zum Projekt relevanten, lokalen Module- und Themes-Repositories beobachten und bei Veränderungen der Dateien direkt in den source-Ordner des lokalen Projektes kopieren.

concurrently ^
"cpx D:\path-to-module\module-1\**\* D:\path-to-shop\source\modules\trw\trwmodule1\ --no-initial --watch --verbose ^" ^
"cpx D:\path-to-module\module-2\**\* D:\path-to-shop\source\modules\trw\trwmodule2\ --no-initial --watch --verbose ^" ^
"cpx D:\path-to-module\module-3\**\* D:\path-to-shop\source\modules\trw\trwmodule3\ --no-initial --watch --verbose ^" ^
"cpx D:\path-to-module\module-4\**\* D:\path-to-shop\source\modules\trw\trwmodule4\ --no-initial --watch --verbose ^" ^
"cpx D:\path-to-theme\theme-1\de\**\* D:\path-to-shop\source\Application\views\theme1\de\ --no-initial --watch --verbose ^" ^
"cpx D:\path-to-theme\theme-1\en\**\* D:\path-to-shop\source\Application\views\theme1\en\ --no-initial --watch --verbose ^" ^
"cpx D:\path-to-theme\theme-1\tpl\**\* D:\path-to-shop\source\Application\views\theme1\tpl\ --no-initial --watch --verbose ^" ^
"cpx D:\path-to-theme\theme-1\out\**\* D:\path-to-shop\source\out\ --no-initial --watch --verbose ^"

Dadurch kann man direkt in den lokalen Module- und Themes-Repositories entwickeln und hat zum direkten Test seine lokale Shop-Installation.
Am Ende des Tages können dann die Veränderungen in den Module- und Themes-Repositories eingecheckt werden. Ein composer update im lokalen Projekt zieht dann die frischen Repositories und legt dann die Dateien aus den Modulen und dem Child-Theme „offiziell“ an.

Mehrere Shopinstanzen gleichzeitig bearbeiten …

Mit phpStorm hätte man einfach je Shop-Installation ein Projekt und je Projekt dann alle relevanten Repositories im Vendor-Ordner. Wechselt man nun das Projekt, einfach alles einchecken und dann im neuen Projekt mit composer update der root-composer.json, die Installation auffrischen.

Das gleiche gilt für die Arbeit ohne phpStorm. Wechsele ich zwischen den Projekten, führe ich im neuen Projekt lokal zuerst ein composer update durch, damit ich die Anpassungen der übergreifenden Module, die ich in dem vorherigen Projekt vorgenommen habe, auch in diesem Projekt zur Verfügung stehen habe.