Menu

4 maart 2022 • Gepubliceerd door ; november 14, 2022 at 2:28 pm Qdraw

Een eigen NPM private feed maken met Azure Devops

In deze blogpost ga ik het hebben over een technische oplossing waarbij je software onderdelen kunt delen tussen verschillende applicaties. Dit is alleen relevant als je meerdere applicaties hebt en hier onderdelen tussen wil delen.

There is an English version of this blog post

TL;DR; (Samenvatting)

In deze blog koppelen we een private NPM-feed (Node Package Manager) om deze te gebruiken tijdens het ontwikkelen, in een container en in de Continuous Integration (CI) pipelines. Dit doe ik om versies te hebben van componenten. Ik gebruik hier de tools Azure DevOps, NPM, Nodejs en Docker. Om er voor te zorgen dat de authenticatie gebruiken we een Oath2 flow waarbij vlak voor de installatie (preinstall) tokens ophalen. Er is een Github repository aanwezig waar een werkend voorbeeld wordt gegeven

Project wensen

Voor een project waar ik aan werk bestaat de wens naar losse NPM registries. NPM staat voor Node Package Manager. NPM wordt gebruikt om javascript gebaseerde projecten online te her gebruiken. Hiervoor wordt de publieke npmjs.com-feed gebruikt. Als je code wil delen tussen een beperkte set gebruikers of teams kun je private user (project) of organisation-scoped packages publiceren naar het npm registry. User (project) scoped betekent dat deze alleen binnen hetzelfde Azure DevOps project wordt gedeeld en als je organisation-scoped kiest wordt deze binnen de hele organisatie gedeeld. Deze private NPM registries hebben als voordeel dat front-end applicaties eigen ontwikkelde software onderdelen kunnen uitwisselen.

Wat is Azure DevOps

Azure DevOps is een tool voor ontwikkelaars services waarmee teams werk kunnen plannen, samenwerken aan code-ontwikkeling en toepassingen kunnen bouwen en implementeren. Azure DevOps ondersteunt een samenwerkingscultuur en een reeks processen die ontwikkelaars, projectmanagers en bijdragers samenbrengen om software te ontwikkelen. Wij gebruiken de pipeline functionaliteiten het meeste. Dat is het centraal compileren van software en het releasen naar omgevingen.

Azure DevOps Artifacts

Naast de pipelines bestaan er ook Artifacts. Met Azure Artifacts kunnen ontwikkelaars pakketten uit verschillende feeds en openbare registers delen en gebruiken. Pakketten kunnen worden gedeeld binnen hetzelfde team, dezelfde organisatie en zelfs openbaar. Azure Artifacts ondersteunt meerdere pakkettypen, zoals NuGet, npm, Python, Maven en Azure Universal Packages.

Zo ziet een feed er bijvoorbeeld uit:



Een eigen NPM private feed maken met Azure Devops ; Overzicht van een private npm feed ; Azure Devops, private feed

Docker containers

Voor het project gebruiken we Docker containers om de software te draaien.
Een container is een manier om software en alle dependencies van software bij elkaar te houden en makkelijk uit te wisselen tussen verschillende omgevingen. Een Docker-containerimage is een lichtgewicht, op zichzelf staand, uitvoerbaar softwarepakket dat alles bevat wat nodig is om een applicatie uit te voeren: code, runtime, systeemtools, systeembibliotheken en instellingen.

Azure’s Windows only oplossing

De officiële tool om de authenticatie naar je private NPM feed te regelen is vsts-npm-auth alleen deze werkt alleen op Windows. Aangezien we in ons project een combinatie van Mac, Windows en Docker werken is dit geen werkbare oplossing in onze situatie.

Personal access tokens (PAT’s)

Helaas werken PAT’s niet goed in combinatie met het ophalen van NPM-packages uit de Azure Artifacts feed. Ik krijg hier 401 errors op en kom er niet verder mee.

Een alternatief: better-vsts-npm-auth

Ik ben verder gaan kijken en kwam het project better-vsts-npm-auth tegen. Deze tool doet een oauth2 authenticatie richting Azure DevOps en zorgt ervoor dat je als gebruiker een valide token hebt om npm packages mee op te halen

In de praktijk: Stappen die vooraf nodig zijn

De volgende stappen heb je nodig om zelf een refresh token te generen.

  1. Voordat je begint: Zorg ervoor dat op je ontwikkelmachine een recente versie van nodejs is geïnstalleerd
  2. Zorg ervoor dat je toegang hebt tot het Azure DevOps Artifacts gedeelte: https://dev.azure.com/YOUR_ORG/_packaging

 

Ik heb onderstaande code snippets verzameld en in een losse repository gezet. Deze is op Github te vinden en daar te downloaden.

 

Maak een nieuwe feed aan in Azure Devops

Binnen Azure DevOps valt dit onder Artifacts klik dan op “+ Create Feed”

Je kunt hier kiezen uit twee verschillende scopes

  1. Project, dus alleen het huidige project
  2. Organization: binnen de huidige Azure DevOps organization

 



Een eigen NPM private feed maken met Azure Devops ; Voeg een nieuwe feed toe ; Azure Devops, Add new feed

Installeer de global package better-vsts-npm-auth

Op je lokale machine installeer je better-vsts-npm-auth als globaal pakket zodat deze vanaf verschillende locaties aangeroepen kan worden

Doe dit middels het volgende commando:


npm i -g better-vsts-npm-auth

 

Setup voor front-end library

Ga naar je front-end library folder en zorg ervoor dat er twee dingen minimaal in staan.

  1. project .npmrc file
    De project npmrc bevat de locaties van je eigen private feeds met een prefix van een pakketnaam. Hieronder geef ik een voorbeeld van hoe ik dat toepas.
  2. package.json bestand
    Dit bevat de namen van alle dependencies die worden gebruikt.

 

1. Library Project .npmrc file

 


@qdraw-components:registry=https://pkgs.dev.azure.com/qdraw/_packaging/demo/npm/registry/
always-auth=true

 

2. Library Project package.json file

 


{
    "name": "@qdraw-components/components",
    "version": "0.0.1-rc.1",
    "description": "Test front-end components",
    "scripts": {
    },
    "author": "",
    "license": "ISC",
    "devDependencies": {
        "typescript": "^4.5.5"
    },
    "dependencies": {
    },
    "main": "dist/cjs/index.js",
    "module": "dist/esm/index.js",
    "files": [
        "dist"
    ],
    "types": "dist/index.d.ts"
}

 

Authenticatie vanaf je ontwikkelmachine

De authenticatie verloopt vervolgens via de Azure DevOps aan de hand van de recent geïnstalleerde tool. Dit doe ik binnen de front-end library folder waar ook het bovenstaande package.json en npmrc bestand staan.


better-vsts-npm-auth



Een eigen NPM private feed maken met Azure Devops ; better-vsts-npm-auth ; Azure Devops, better-vsts-npm-auth, npmrc

 

Als ik bovenstaande url kopieer in de browser krijg ik het volgende scherm te zien. Druk op Accept om verder te gaan



Een eigen NPM private feed maken met Azure Devops ; Stateless VSTS NPM OAuth ; Azure Devops

 

Wanneer je op Accept klikt krijg je het Succes scherm te zien. Op dit scherm staat een command met een unieke refresh token.

 



Een eigen NPM private feed maken met Azure Devops ; Copy this command and run it in your terminal ; Azure Devops, terminal

 

We doen twee dingen met het token: Het command uitvoeren:


better-vsts-npm-auth config set refresh_token eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Im9PdmN6NU1fN3AtSGpJS2xGWHo5M3VfVjBabyJ9.eyJuYW1laW

Kopieer het token gedeelte zonder: better-vsts-npm-auth config set refresh_token. Zet dit als environment variable
DEMO_NPM_REFRESH_TOKEN, hoe je dat doet staat hieronder beschreven. Deze variable hebben we later nodig in Docker.

 

1. Op Mac OS voeg je het volgende toe aan je .zprofile of .zshrc:


nano ~/.zprofile

export DEMO_NPM_REFRESH_TOKEN="LongTokenHere"

Deze instellingen zijn pas actief na het herstarten van je terminalvenster

 

2. Op Windows voeg je het volgende toe:

  • Druk de volgende toetscombinatie in: Windows + R
  • Type: `sysdm.cpl`
  • Click op de Advanced tab
  • Iets boven OK & Cancel click op Environment Variables…
  • Onder System Variable: Press New
  • DEMO_NPM_REFRESH_TOKEN
  • En de token (without the better-vsts prefix)
  • Druk Ok om te bevestigen

Deze instellingen zijn pas actief na het herstarten van je Powershell venster

 

Controleer of je in de juiste omgeving bent ingelogd

Controleer of de app: “Stateless VSTS NPM OAuth” aanwezig is op: https://dev.azure.com/YOUR_ORG/_usersSettings/authorizations

Staat de app daar niet, dan ben je waarschijnlijk verbonden met de verkeerde organisatie. Als dit het geval is, probeer dan de url te kopiëren en te plakken in een browservenster dat zich in de privé- of incognito modus bevindt.



Een eigen NPM private feed maken met Azure Devops ; Controleer of de app in de lijst staat ; Azure Devops, Authorized Oauth Apps

Bij authenticatie problemen

Mochten er problemen zijn met de authenticatie verwijder dan de volgende bestanden uit je home folder. Op Mac OS zou een voorbeeld dit /Users/dion kunnen zijn en op Windows C:\Users\Dion.

  1. “.vstsnpmauthrc”
  2. “.npmrc”
  3. De environment variable “DEMO_NPM_REFRESH_TOKEN”

Beide bestanden bevatten inloggegevens en kunnen veilig worden verwijderd. Probeer het daarna opnieuw. Hieronder toon ik dat de twee bestanden aanwezig zijn



Een eigen NPM private feed maken met Azure Devops ; Je home folder, deze bestanden kun je verwijderen om opnieuw te ; Azure Devops, home folder

 

NPM Publish vanuit front-end library folder

Vanuit de front-end library folder doen we een publish naar Azure DevOps. Zorg ervoor dat bij het publiceren elke keer je in de package.json een uniek oplopend versienummer hebt.



Een eigen NPM private feed maken met Azure Devops ; NPM Publish ; Azure Devops, npm, node package manager, NPM Publish

 

Nu staat deze package in de private npm feed en dat ziet er als volgt uit.



Een eigen NPM private feed maken met Azure Devops ; Nieuw aangemaakte feed ; Azure Devops, private feed

Unieke prefix wanneer je meerdere private feeds hebt

In ons geval gebruiken we meerdere npm feeds voor verschillende onderdelen. In het bovenstaande voorbeeld gebruik ik de prefix: “@qdraw-components” en een slash om deze prefix af te sluiten. Het is belangrijk om de naam op deze manier op te bouwen, dan kun je dit namelijk aangeven in het project npmrc bestand. Mocht je er een feed naast hebben voor icons dan kan deze niet dezelfde prefix hebben. De prefix moet dus uniek zijn en beginnen met een apenstaartje.

 

Docker Applicatie setup

In de applicatie waar de front-end library wordt gebruikt doen we een volgende setup: Hetzelfde. npmrc bestand staat in de applicatiemap.

De project .npmrc file


@qdraw-components:registry=https://pkgs.dev.azure.com/qdraw/_packaging/demo/npm/registry/
always-auth=true

Daarnaast heeft dit project een eigen package.json

package.json


{
  "name": "use_app",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "preinstall": "node scripts/preinstall.js"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "@qdraw-components/components": "^0.0.1-rc.1"
  }
}

In de preinstall wordt op basis van een refresh token een access token opgehaald zodat je private packages opgehaald en geïnstalleerd kunnen worden

Het bestand scripts/preinstall.js is te vinden op Github

In de Dockerfile wordt het bestand scripts/cleanauth.js aangeroepen om de inloggegevens uit de container te verwijderen.

scripts/cleanauth.js


#!/usr/bin/env node
 
// remove tokens from docker image
 
const fs = require('fs')
const path = require('path');
 
function UserProfileFolder() {
    let userProfileFolder = "~";
    if (process.env.CSIDL_PROFILE) {
        userProfileFolder = process.env.CSIDL_PROFILE;
    }
    if (process.env.HOME) {
      userProfileFolder = process.env.HOME;
    }
    return userProfileFolder;
}
 
const userProfileFolder = UserProfileFolder();
 
const vstsNpmauthRcFilePath = path.join(userProfileFolder, '.vstsnpmauthrc');
if (fs.existsSync(vstsNpmauthRcFilePath)) {
    fs.unlinkSync(vstsNpmauthRcFilePath)
}
 
const userNpmRcFilePath = path.join(userProfileFolder, '.npmrc');
if (fs.existsSync(userNpmRcFilePath)) {
    fs.unlinkSync(userNpmRcFilePath)
}

Dit project heeft de volgende Dockerfile

Dockerfile


# Install dependencies only when needed
FROM node:16-alpine AS deps
ARG YOURPROJECT_NPM_REFRESH_TOKEN=default
ARG AZURE_AUTH_TOKEN=default
 
RUN apk add --no-cache libc6-compat
WORKDIR /app
 
ENV YOURPROJECT_NPM_REFRESH_TOKEN=$YOURPROJECT_NPM_REFRESH_TOKEN
ENV AZURE_AUTH_TOKEN=$AZURE_AUTH_TOKEN
 
COPY use_app/scripts/preinstall.js ./scripts/preinstall.js
COPY use_app/scripts/cleanauth.js ./scripts/cleanauth.js
COPY use_app/package.json ./
 
RUN node scripts/preinstall.js
RUN npm ci --prefer-offline
 
RUN node scripts/cleanauth.js

Deze Dockerfile kan op twee manieren worden aangeroepen zodat dit een docker container wordt.

 

1. Docker build command


cd root_of_solution
docker build -f use_app/Dockerfile . --no-cache --build-arg DEMO_NPM_REFRESH_TOKEN=${DEMO_NPM_REFRESH_TOKEN}

 

2. Of als Docker compose

docker-compose.yml


version: '3.4'
 
services:
  demo.use.app:
    image: ${DOCKER_REGISTRY-}demouseapp
    build:
      context: .
      dockerfile: use_app/Dockerfile
      args:
        - DEMO_NPM_REFRESH_TOKEN=${DEMO_NPM_REFRESH_TOKEN}
    environment:
      - DEMO=true
    ports:
      - "19443:9443"

 

Docker draait op een lokale omgeving



Een eigen NPM private feed maken met Azure Devops ; Docker ; Azure Devops, npm, node package manager, Docker, container

 

Azure DevOps Pipeline

Om Azure DevOps te configureren gebruik je yaml files, hierin staan alle stappen welke de build pipeline gaat uitvoeren
Zie hiervoor: azure/azure-pipelines.yml en azure/templates/build-docker.yml



Een eigen NPM private feed maken met Azure Devops ; Azure Devops Build pipeline, 403 ; Azure Devops, Docker, container, Azure Devops Build pipeline, 403

Als je een organisation-scoped packages uit de Azure Artifact gebruikt dan krijg je de volgende foutmelding:

“npm ERR! 403 403 Forbidden – GET https://pkgs.dev.azure.com/YOURENV/_packaging/demo/npm/registry/@qdraw-components%2fcomponents – User d9af02c4-b000-4fd4-9754-68706813d7ca”

Om deze te verhelpen voeg je de build user toe aan de private feed



Een eigen NPM private feed maken met Azure Devops ; Volg de pijl ; Azure Devops, private feed

In de onderstaande schermafbeelding zie je hoe ik dat doe.



Een eigen NPM private feed maken met Azure Devops ; Klik Add users/group ; Azure Devops, private feed

Build succesvol



Een eigen NPM private feed maken met Azure Devops ; Azure Devops draait goed ; Azure Devops, Docker, container

Conclusie

Het was een interessante zoektocht naar de mogelijkheden van Azure DevOps en dan met name hoe ze dit onderdeel in elkaar hebben gestoken. Omdat er zo weinig van op internet te vinden was heb ik veel zelf uitgezocht en in deze blog heb ik getracht om mijn kennis zo goed mogelijk uit te schrijven.

En het pakketje is succesvol bezorgd…



Een eigen NPM private feed maken met Azure Devops ; Pakket bezorger ; straatleven, straat, pakketbezorger, post, bezorger

Fout senario: Verbonden met de verkeerde tenant

Als je de volgende melding ziet in de preinstall.js: You are probably connected to the WRONG tenant
Ga dan naar de switcher in Azure Devops https://app.vsaex.visualstudio.com/me en selecteer aan de linkerkant het juiste bedrijf. Ga vervolgens weer terug naar oauth provider, dit is in de meeste gevallen: stateless-vsts-oauth.azurewebsites.net om een nieuw refresh token op te halen. Plaats deze weer als environment variablen en herstart het venster om het daarna opnieuw te proberen

Optionele stap: Zelf hosten van de stateless-vsts-oauth provider

Mocht je de stateless-vsts-oauth zelf willen hosten bijvoorbeeld niet afhankelijk te zijn van: stateless-vsts-oauth.azurewebsites.net dan is de code te vinden op:
github.com/zumwald/stateless-vsts-oauth

Hiervoor dien je een App te registeren op Azure Devops. Ga naar Azure Devops Register oauth app
Vul hier je bedrijfsnaam en contactgegevens in. Bij Application Information vul je bij Application website het adres waar de app gehost wordt. Dit adres moet publiek beschikbaar zijn. Verder de Authorization callback URL is het zelfde adres met als toevoeging /oauth-callback. De Authorized scopes zijn Packaging (read and Write). Verder hoef je niks aan te vinken. Klik op Create Application om verder te gaan. De App ID en de Client Secret ben je nodig in volgende stappen. Om later bij dit overzicht te komen ga je naar https://app.vsaex.visualstudio.com/me

Gebruik het volgende format om de environment variablen te zetten


CLIENT_ID : YOUR_CLIENT_ID_GUID
CLIENT_SECRET : eyJ0eXAi....
PORT : 8080
WEBSITE_HOSTNAME : devopsauth.example.com

De App ID wordt ook gebruikt in de preinstall.js en het adres waar de oauth provider is gehost. Belangrijk is dat je deze hier ook in veranderd

Tags: , ,

Gecategoriseerd in:

Dit bericht is geschreven door: Dion

Lees ook deze blogs