nut.js – Rekapitulace za dva roky

nut.js – rekapitulace za dva roky

Před více než dvěma lety jsem začal vyhodnocovat možnosti automatizace desktopů pomocí Node.js. Porovnával jsem různé existující knihovny, ale nakonec mě žádná z nich nepřesvědčila. Všechny měly své pro a proti, ale žádný z nich nesplňoval všechny mé požadavky, což kde:

  • Knihovna je aktivně udržována
  • Rychlá a snadná instalace
  • Plně kompatibilní s různými platformami
  • Poskytuje možnosti shody obrázků

Zatímco první tři požadavky mohly být splněny, čtvrtý vyřadil každou jednotlivou knihovnu, kterou jsem kontroloval. Zdálo se, že žádná knihovna pro automatizaci stolního počítače pro uzel neposkytovala funkce pro porovnávání obrázků – a tehdy jsem se rozhodl, že si ji vytvořím sám.
O dva roky později a stále aktivně udržuji nut.js, takže jsem si řekl, že by mohl být vhodný čas na rekapitulaci toho, co se během těchto dvou let stalo.

The Early Days – nativní sada nástrojů ui

První prototyp, který jsem dal dohromady, dostal pracovní název native-ui-toolkit . Kombinoval robot-js pro interakce na úrovni OS (uchopení obsahu obrazovky, vstup z klávesnice / myši, přístup do schránky) s opencv4nodejs pro porovnávání obrázků. Přestože se jedná o počáteční funkční prototyp, tento první návrh již odhalil některé zásadní problémy.
robot-js podporoval uzel pouze do verze 8, což byl docela průšvih, protože uzel 10 se stal nejnovější verzí LTS 30. října 2018. Navíc se zdálo, že se vývoj zastavil (a jak jsem si dnes ověřil, od března nepřibylo žádné nové vydání 2018).
Druhý hlavní problém nastal s opencv4nodejs . Vyžaduje to buď správně nainstalovanou verzi OpenCV ve vašem systému, nebo vám alternativně dává možnost nechat opencv4nodejs zkompilujte OpenCV za vás. Obě možnosti si nepohrály s mým požadavkem na rychlou a snadnou instalaci. Buď musí uživatel bojovat s instalací správného verze OpenCV, což není stejně snadné na všech platformách, nebo si nechat knihovnu znovu zkompilovat OpenCV při instalaci, což vyžaduje kompletní nástrojový řetězec C++ a trvá 30+ minut.
V neposlední řadě oba opencv4nodejs a robot-js jsou nativní doplňky uzlů. V podstatě se zde tedy zabýváme sdílenými knihovnami, což znamená, že musí být poskytovány pro každou cílovou platformu a v závislosti na použité technologii i verzi cílového uzlu. Protože obě knihovny neposkytovaly způsob, jak poskytnout předem sestavené binární soubory ihned po instalaci, jediným řešením v té době bylo jejich překompilování při instalaci. To zase vyžadovalo C/C++ toolchain a fungující instalaci Pythonu 2. Opět to není můj typ ”rychlé a snadné instalace” .
Čelit těmto problémům však vede k jednomu z nejzásadnějších návrhových rozhodnutí týkajících se architektury nut.js.
Namísto šíření balíčků třetích stran po celé knihovně jsou závislosti omezeny na takzvané „balíčky poskytovatelů“ , které se zabývají knihovními specifiky. Od této chvíle se používají pouze uživatelem definované typy, které plně skryjí jakoukoli externí závislost.

Tito poskytovatelé se používají ve vrstvě adaptéru, kde je lze kombinovat a přiřazovat k implementaci požadované funkce. Uživatelské rozhraní API se spoléhá pouze na tyto adaptéry. Tímto způsobem nový / jiný poskytovatel nikdy nebude moci vyžadovat změny v uživatelském rozhraní API a změny jsou omezeny na vrstvu adaptéru (maximálně). Zpočátku to může znít jako skvělý příklad přehnaného inženýrství, ale zpětně se to ukázalo jako jedno z nejlepších návrhových rozhodnutí, které jsem s nut.js udělal. Podle tohoto schématu jsem implementace nativního poskytovatele změnil zatím třikrát, což vyžadovalo minimální úsilí.

Vyrůstat – odstěhovat se

To, co začalo jako jediné úložiště pod mým účtem GitHub, se brzy rozrostlo do specializované organizace s vlastním úložištěm. Vytvořil jsem plány, jak pokračovat ve vývoji na nut.js, a rozhodl jsem se seskupit nadcházející repozitáře pod organizaci nut-tree GitHub. Před přesunutím repozitáře jsem zavrhl robot-js ve prospěch robotjs, podobné knihovny, která poskytovala předpřipravené binární soubory, takže nebylo potřeba stavět na instalaci.
Díky své vlastní organizaci a repo si nut.js také vysloužil své vlastní logo:

Jediná věc, se kterou jsem stále bojoval, bylo, jak bych mohl poskytnout balíček opencv4nodejs připravený k použití. Jak již bylo zmíněno, instalace správné verze OpenCV může být zdlouhavá a moje pochopení skvělé použitelnosti vyžadovalo nějak způsob instalace lib bez zahrnutí kroku kompilace. Takže kromě dodání předkompilované verze OpenCV jsem musel také poskytnout předkompilované vazby pro různé platformy a verze uzlů, protože opencv4nodejs používá nan pro své vazby.

Stavěl jsem na tom, co již poskytl, rozvětvil jsem jak opencv4nodejs, tak npm-opencv-build. Nevyžadoval jsem celé OpenCV, takže jsem se ponořil do jeho sestavení konfigurace, dokud to nevyhovovalo mým potřebám, a začal jsem konfigurovat CI potrubí. Při běhu na CI by měly být balíčky specifické pro platformu obsahující předkompilovanou verzi OpenCV publikovány podle @nut-tree/opencv-build-${process.platform} systém. První krok správným směrem.

Tyto balíčky OpenCV wrapper vydláždily cestu pro odesílání plně předem sestavených OpenCV vázání. Můj fork opencv4nodejs, opencv4nodejs-prebuilt by nainstaloval knihovny OpenCV pro současnou cílovou platformu a propojil by se s nimi během sestavování. Po přečtení a učení *hodně* o jemných rozdílech týkajících se propojování na macOS, Linux a Windows jsem upravil proces sestavování opencv4nodejs způsobem, který umožňoval dodávat předkompilované vazby uzlů včetně správné, předem sestavené knihovny OpenCV. Díky Travis CI a Appveyor jsem schopen spustit v současnosti celkem 39 úloh pro předpřipravení těchto vazeb, podporující verze uzlů>=10 a Electron>=4 na třech platformách.

Průběžná změna

Jeden velký problém vyřešen, čas na další! Node 12 se měl stát novou verzí LTS, takže mým cílem bylo přirozeně podporovat také uzel 12. Vývoj robotjs se však zastavil. Zdálo se, že původní správce se posunul dál a od roku 2018 nedošlo k řádnému vydání.

Tváří v tvář tomuto problému jsem se rozhodl, že se o to postarám sám a rozdělil projekt. Jakmile jsem se s projektem seznámil, uvědomil jsem si, že podpora uzlu 12 znamená víc než jen aktualizované nastavení CI pro předběžná sestavení. Protože robotjs také používal nan pro své vazby, vyžadoval změny kódu, aby zůstal kompatibilní s uzlem 12.
Vzhledem k této skutečnosti jsem se rozhodl udělat skok a přešel z nan na N-API. Tímto způsobem jsem také změnil sestavovací systémy a nahradil jsem node-gyp cmake-js. Výsledkem všech těchto změn je libnut, který díky stabilitě ABI N-API podporuje budoucí verze uzlů ihned po vybalení.

Po migraci na libnut vypadá aktuální strom závislostí nut.js takto:

Neustálé zlepšování

Nyní, když má nut.js pevný základ, nastal čas na vylepšení.
V prvním kroku jsem přidal dokumentaci.
Kromě vylepšeného souboru readme jsem také přidal automaticky generované dokumenty API, které jsou hostovány prostřednictvím stránek GitHub.

Další věc, kterou jsem řešil, byly předběžné verze.
Každý tlak na vývoj nyní spouští předběžnou verzi, která publikuje vývojovou verzi na npm.
Kdykoli je podána nová značka, bude zveřejněna stabilní verze.
Stabilní verze jsou k dispozici pod výchozím latest vývojové verze jsou publikovány pod next štítek.
Po cestě jsem neustále vylepšoval nastavení CI, abych si usnadnil život.

Jako třetí vylepšení jsem přidal úložiště vzorků.
Toto monorepo obsahuje několik balíčků, které demonstrují různé případy použití nut.js.
Ukázky sahají od interakce klávesnice a myši až po integraci Jest a Electron.

New Shores

Nut.js zatím zabalil clipboardy, libnut a opencv4nodejs-prebuilt do (podle mého názoru) pěkného API.
Zpočátku byl libnut jen port robotjs, takže poskytoval přesně stejnou funkcionalitu.
A protože jsem chtěl, aby byl libnut skutečně kompatibilní napříč platformami, nové funkce musely buď fungovat na všech platformách, nebo by se neměly přidávat, takže když jsem začal pracovat na přidání funkce zvýrazňování plochy, najednou jsem zjistil, že prohrabávám Xlib, Win32 a dokumentaci AppKit, psaní C/C++ i Objective-C/Objective-C++.
Vzrušující zážitek, který mě opravdu rozesmál jako malé dítě na Štědrý večer, když jsem viděl, jak se na každém nástupišti objevují okna!

Přesně to samé se stalo, když jsem přidal podporu pro interakce s okny.
Schopnost určit otevřená okna a jejich obsazenou oblast obrazovky dláždí cestu pro další funkce, které mě opět rozesmály před mým počítačem!
Testování této funkce mě také opravdu přimělo ocenit ekosystém JavaScriptu.
To, co se zpočátku zdálo jako docela obtížný úkol, bylo možné dosáhnout jedinou implementací pro všechny platformy spuštěním aplikace Electron za běhu během testu.
Jediný test nyní ověřuje, že moje nativní implementace funguje na každé platformě – není to úžasné?

Závěr

Jak to tedy jde po dvou letech?

Abych byl upřímný, stále mě práce na nut.js velmi baví hodně!
Zpočátku jsem automatizoval poměrně hodně, takže se nyní mohu soustředit na funkce a opravy chyb.

Jsem také stále spokojený se svými rozhodnutími ohledně návrhu API.
Návrh API je těžký a myslím, že nemůžete udělat radost všem, ale mě osobně to baví, takže za mě!

Podařilo se mi splnit svůj vlastní požadavek na rychlou a snadnou instalaci na každé platformě tím, že jsem strávil nějaký čas navíc přednastavením, na což jsem dodnes hrdý!

Jak jsem již zmínil, u nut.js hraje hlavní roli automatizace.
Ale nejenže jsem spoustu věcí zautomatizoval, ale také jsem trávil čas prací na správné testovací infrastruktuře pomocí více systémů CI a vícestupňových kanálů, abych se ujistil, že věci neporuším.
Být schopen vydat rychle a s jistotou se opravdu vyplatí investovat!

V neposlední řadě jsem byl velmi nadšený, když jsem si všiml, že RedHat vybral nut.js pro svůj tester rozšíření vscode.
Říkejte mi fanboy, ale vidět, jak společnost, kterou znám téměř 20 let pro jejich distribuci Linuxu a práci s otevřeným zdrojovým kódem, začíná používat můj framework, je pro mě docela věc!

Dva roky a pořád to jde! 💪