Wie Docker Container die Einrichtung der SPFx Umgebung überflüssig machen
Ohne Installation mehrere SPFx/Node.js Versionen auf einer Umgebung bedienen
Das SharePoint Framework (SPFx) ist ein Package, welches sehr oft aktualisiert wird. Das ist auf der einen Seite sehr gut, auf der anderen Seite ist es aber auch ein Problem. Denn, je nach Version, verwendet es unterschiedliche node, npm, TypeScript und React Versionen (und natürlich noch andere Packages).
Die erste SPFx Version (1.0.0), die vor ziemlich genau 5 Jahren veröffentlicht wurde, benötigt Node v6 LTS und npm v3.
Die SPFx Version 1.4.1 (für SP 2019 On-Premises) hat schon Node v8 LTS unterstützt und auch npm v4.
Die gestern (17. Februar 2022) veröffentlichte SPFx Version 1.14 benötigt mittlerweile mindestens Node v12 LTS. Eine Auflistung aller SPFx Versionen und welche Node, npm etc. Version diese benötigt, findest Du hier.
Ist doch kein Problem, könnte man denken. Dann wird eben immer die neuste Version installiert bzw. auf die neuste Version aktualisiert. Nun, ganz so einfach ist es dann doch nicht. Was, wenn man noch "alte" Projekte hat? Projekte, die man weiter betreuen muss und die z. B. für SP2019 oder sogar SP2016 entwickelt wurden. Denn zwei unterschiedliche Node Versionen gleichzeitig zu installieren ist nicht möglich auf einem Computer. Klar, man kann eine VM verwenden. Aber dann hat man viele VMs, die auch noch unnötig CPU/RAM und vor allem auch Festplattenspeicher verbrauchen. Und jede davon muss man einrichten.
Docker Container einrichten
Mein nächstes Projekt (kein SharePoint) soll Docker verwenden. Ist mir natürlich ein Begriff, immerhin spricht jeder darüber. Aber Erfahrung hatte ich keine damit. Um nicht ohne Kenntnisse über Docker mit diesem Projekt zu beginnen, habe ich mir vor zwei Tagen ein super gutes Video auf Youtube (auf Deutsch), von Golo Roden, angeschaut. Der hat es wahnsinnig gut erklärt und ich habe jetzt wahrscheinlich mehr als nur Basis-Kenntnisse zu Docker. Danach habe ich mir gedacht, eigentlich könnte man doch sowas auch für SPFx erstellen. Dann müsste man die Umgebung überhaupt nicht mehr einrichten (spart Zeit) und man kann unterschiedliche Versionen gleichzeitig verwenden. Bevor ich mir mein eigenes Dockerfile erstelle, habe ich natürlich geschaut, ob schon ein Docker Image existiert für SPFx. Und siehe da, es gibt eines. Das wollte ich sofort ausprobieren. Ich werde übrigens nicht darauf eingehen, wie Du Docker einrichtest/installierst oder was es ist, wie es funktioniert oder wie man damit umgeht. Das ist für den Artikel auch nicht relevant. Es werden auch keine Docker Kenntnisse benötigt.
Vor zwei Tagen war SPFx 1.14 noch nicht offiziell veröffentlicht. Deswegen habe ich mir das Image für SPFx 1.13 heruntergeladen. Die Dokumentation zu dem Docker Image ist eigentlich ganz gut, jedoch bin ich über ein Paar Hindernisse gestolpert. Schon im ersten Schritt!
in Docker Settings > Shared Drives verify that the drive where you create your projects is shared
Diesen Einstellung gibt es bei mir nicht! Das hat mehrere Gründe. Den "Shared Drive" gibt es seit längerem nicht mehr unter Docker Desktop. Denn eigentlich müsste es unter Docker Settings > Resources > File Sharing
zu finden sein.
Ja, ich weiß, ich muss mein Docker Desktop updaten 😜
Doch auch diese Einstellung ist bei mir nicht zu finden. Man muss dazu sagen, dass ich ein Windows 10 Betriebssystem habe und WSL 2 (Windows Subsystem for Linux) verwende.
Unter WSL 2 benötigt man ein File Share nicht, weil sozusagen alles auf dem lokalen Computer ausgeführt wird statt auf einem Hyper-V backend.
Nachdem ich das herausgefunden habe, wollte ich mit den weiteren Schritten beginnen. Also habe ich einen Ordner erstellt. Dort dann den Befehl wie in der Dokumentation beschrieben eingegeben:
docker run -it --rm --name spfx-helloworld -v ${PWD}:/usr/app/spfx -p 4321:4321 -p 35729:35729 m365pnp/spfx
Ich erkläre Dir kurz die Befehle:
docker run
erstellt einen neuen Container
-it
steht für interactive
, d. h. man bleibt nach dem erstellen "im Container". Das ist meistens eine Terminal/Bash Fenster
--rm
bedeutet, dass nach dem beenden des Containers, dieser auch gelöscht wird
--name
ist der Name des Containers
-v
ist das Volume mapping. Dabei wird der aktuelle lokale Pfad (${PWD}
) auf den Containerpfad usr/app/spfx gemapped
-p
steht für Port. Hiermit wird der lokale Port 4321
mit den Container Port 4321
verknüpft. Und der Port 35729
mit dem Container Port 35729
.
Und zum Schluss wird noch der Name des Images angegeben ==> m365pnp/spfx.
Nun wird der Container erstellt und es öffnet sich eine Shell. Diese Shell ist die Shell des Containers. Deswegen musst Du dort auch Deine ganzen yo
, npm
, gulp
und sonstige Befehle angeben und nicht auf dem lokalen Computer.
Das bedeutet, gib nach dem yo @microsoft/sharepoint
-Befehl Deine ganzen npm
, gulp
usw. Befehle ein. Erst wenn Du zu 100% fertig bist (z. B. Package veröffentlich, Testing beendet etc.) gibst Du exit
ein. Danach ist der Container beendet und wird aufgrund des --rm
Befehls auch sofort gelöscht. Aber keine Angst, nicht das Projekt. Das liegt schließlich auf Deinem lokalen Computer. Wenn Du später wieder mit dem Projekt arbeiten willst, musst Du den Container wie oben beschrieben einfach erneut erstellen (im Projektverzeichnis).
Theoretisch könntest Du nun mit gulp serve
das Projekt starten. Doch das wird beim ersten Mal nicht funktionieren. Dies ist auch in der Dokumentation des Docker Images beschrieben. Denn Du musst zuerst die serve.json
Datei, die sich im config
Ordner befindet, bearbeiten. Dazu muss man den hostname
auf 0.0.0.0
setzen.
{
"$schema": "https://developer.microsoft.com/json-schemas/core-build/serve.schema.json",
"port": 4321,
"hostname": "0.0.0.0",
"https": true,
"initialPage": "https://yourtenant.sharepoint.com/_layouts/workbench.aspx"
}
Da ich - wie zu Beginn beschrieben - die SPFx Version 1.13 verwendet habe, musste ich laut Anleitung auch noch eine JavaScript Datei in Zeile 393 bearbeiten, die hier liegen soll:
node_modules\@microsoft\spfx-heft-plugins\lib\plugins\webpackConfigurationPlugin\WebpackConfigurationGenerator.js
Doch auch das ist nicht ganz richtig. Denn diese Datei liegt eigentlich unter:
node_modules\@microsoft\sp-build-web\node_modules\@microsoft\spfx-heft-plugins\lib\plugins\webpackConfigurationPlugin\WebpackConfigurationGenerator.js
Diesen Schritt muss man in der neuen SPFx Version 1.14 nicht mehr durchführen. Es wird nun von Microsoft selbst geprüft, ob man sich in einem Container befindet oder nicht (anhand der ipAddress-Eigenschaft in der serve.json-Datei). Falls Du eine andere Version verwendest, schau bitte in der Dokumentation des Docker Images nach, ob Du noch etwas beachten musst. Übrigens wurde in der Version 1.14 auch mein gemeldeter Bug mit dem Fullmask Permission Check behoben 😊
Jetzt könnte man gulp serve
ausführen und die SharePoint workbench aufrufen. https://{tenant}.sharepoint.com/_layouts/15/workbench.aspx
Doch Du wirst feststellen, dass Deine Komponente nicht vorhanden ist. Dieser Punkt wird in der Dokumentation der Docker Image überhaupt nicht aufgeführt. Die Ursache ist, dass das Zertifikat nicht vertrauenswürdig ist. Dabei ist es egal, ob Du auf Deiner lokalen Umgebung bereits gulp trust-dev-cert
ausgeführt hast oder ob Du das bereits im Container gemacht hast. Damit die SPFx Komponente angezeigt wird, muss die URL https://localhost:4321
aufgerufen werden (auf der lokalen Umgebung). Danach musst Du dem Zertifikat vertrauen.
Jetzt sollte Deine Komponente auch in der workbench (oder wo auch immer) angezeigt werden.
SPFx Fast Serve im Docker Container
Da ich ein sehr großer Fan von dem spfx-fast-serve Package bin (falls Du es nicht kennst, musst Du es unbedingt verwenden!). Deswegen habe ich auch in diesem Container versucht spfx-fast-serve
anzuwenden. Es gab dazu zwar ein GitHub issue, doch war der "workaround" für mich so nicht akzeptabel. Deswegen habe ich versucht das Problem anders zu beheben. Seit gestern gibt es auch hierfür eine Lösung. Sergei Sergeev, der Autor des Packages, wird meine Lösung auch offiziell in seiner Readme aufnehmen.
Um spfx-fast-serve
in Deinem Container zu verwenden, musst Du nur die fast-serve/webpack.extend.js
öffnen und die Konstante webpackConfig
anpassen:
const webpackConfig = {
devServer: {
host: '0.0.0.0',
publicPath: 'https://0.0.0.0:4321/dist/',
sockHost: 'localhost',
watchOptions: {
aggregateTimeout: 500, // delay before reloading
poll: 1000 // enable polling since fsevents are not supported in docker
}
},
output: {
publicPath: 'https://0.0.0.0:4321/dist/'
}
}
Jetzt funktioniert der npm run serve
Befehl auch im Container.
Eigenes Docker image. 70% Kleiner und mit spfx-fast-serve
Das offizielle Docker image ist schon sehr gut und hilfreich. Was mir jedoch nicht gefallen hat, war die Imagegröße. Die beträgt nämlich 1.22 GB und ich wollte nicht immer das spfx-fast-serve
Package zusätzlich installieren müssen. Deswegen habe ich mir überlegt, ein eigenes Image zu erstellen. Dazu habe ich in dem Youtube Video gelernt, dass das Linux Alpine Betriebssystem um einiges kleiner ist, weil es ohne bestimmte Packages ausgeliefert wird, welche die anderen Linux Distributionen verwenden. Das heißt, das bei dem SPFx Docker Image die Linux Distribution so viel Platz einnimmt bzw. das verwendete Node Image diese Linux Version verwendet. Man kann aber Node auch in der Alpine Version verwenden. Ich habe also das Original Dockerfile kopiert und es "Alpine"-Ready gemacht. Denn die Befehle für Alpine unterscheiden sich dabei. Ich habe es sozusagen übersetzt.
Herausgekommen ist dabei folgendes Dockerfile mit spfx-fast-serve:
FROM node:14.19.0-alpine3.15
EXPOSE 5432 4321 35729
ENV NPM_CONFIG_PREFIX=/usr/app/.npm-global \
PATH=$PATH:/usr/app/.npm-global/bin
VOLUME /usr/app/spfx
WORKDIR /usr/app/spfx
RUN apk add sudo && \
apk --no-cache add shadow && \
echo '%wheel ALL=(ALL) ALL' > /etc/sudoers.d/wheel && \
adduser --home "$(pwd)" --disabled-password --ingroup wheel --shell /bin/ash spfx && \
usermod -aG wheel spfx && \
chown -R spfx:wheel /usr/app
USER spfx
RUN npm i -g gulp@4 yo @microsoft/generator-sharepoint@1.13.1 spfx-fast-serve
CMD /bin/ash
Wenn man die Image-Größen vergleicht, ist mein Image um 70% kleiner, und zwar nur noch 400MB
In diesem Container musst Du die gleichen Schritte machen, die ich oben beschrieben habe. Nach dem erstellen der Solution kannst Du einmalig spfx-fast-serve
ausführen (installiert werden muss es nicht mehr, das wurde im Docker Image gemacht) und danach npm i
und schon kannst Du npm run serve
verwenden.
That's it. Happy Coding ;)