NPM Packages über den GitHub NPM Feed veröffentlichen

GitHub bietet eigene Package Feeds für NPM, NuGet usw. an. Diese bieten eine Alternative zu den offiziellen Feeds und haben den Vorteil, dass alles direkt in GitHub integriert ist. Du musst also keine Accounts bei den offiziellen Feeds erstellen und Secrets für den Zugriff in deinen CI/CD Pipelines hinterlegen. In diesem Beitrag zeige ich dir, wie du in wenigen Schritten mit einer GitHub Action ein NPM Package über den GitHub NPM Feed veröffentlichen kannst.

Der GitHub Actions Workflow

Damit wir ein NPM Package veröffentlichen können, müssen wir es natürlich zunächst erstellen. Dazu erstellen wir einen GitHub Actions Workflow. Als Basis dient der Workflow aus einem früheren Beitrag, den ich zum Erstellen einer Static Site verwendet hatte:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
name: build-and-publish

on: push

jobs:
  build-and-publish:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout Code
        uses: actions/[email protected]
      - name: Restore Node Modules
        uses: actions/[email protected]
        with:
          path: node_modules
          key: ${{ runner.os }}-modules-${{ hashFiles('**/yarn.lock') }}
          restore-keys: |
                        ${{ runner.os }}-modules-
      - name: Install Dependencies
        run: yarn --frozen-lockfile
      - name: Build Package
        run: yarn build

In diesem Fall erstellt der Befehl yarn build anstatt der Static Site das NPM Package. Auf die Details gehen wir hier nicht ein, da diese davon abhängig sind mit welcher Toolchain du dein NPM Package erstellst. Wir gehen für den weiteren Verlauf davon aus, dass das NPM Package im Verzeichnis dist/package erstellt wurde. Wichtig ist lediglich, dass in diesem Verzeichnis schlussendlich eine Datei package.json existiert, die den Names des Packages und die Version enthält:

1
2
3
4
{
  "name": "test-github-npm-feed",
  "version": "1.0.0"
}

NPM bzw. Yarn konfigurieren

Als nächstes müssen wir NPM bzw. Yarn so konfigurieren, dass beim Veröffentlichen des Packages der richtige Package Feed bzw. die richtige Registry verwendet wird. Normallerweise verwenden NPM und Yarn den standard Package Feed von NPM, welcher über die URL https://registry.npmjs.org angesprochen wird. Wir wollen unser Package jedoch im GitHub Package Feed veröffentlichen, welcher unter der URL https://npm.pkg.github.com verfügbar ist. Damit beim Veröffentlichen des Packages auch dieser Feed verwendet wird, konfigurieren wir dies über die entsprechende GitHub Action actions/setup-node:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
name: build-and-publish

on: push

jobs:
  build-and-publish:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout Code
        uses: actions/[email protected]
      - name: Restore Node Modules
        uses: actions/[email protected]
        with:
          path: node_modules
          key: ${{ runner.os }}-modules-${{ hashFiles('**/yarn.lock') }}
          restore-keys: |
                        ${{ runner.os }}-modules-
      - name: Install Dependencies
        run: yarn --frozen-lockfile
      - name: Build Package
        run: yarn build
      - name: Prepare Publish
        uses: actions/[email protected]
        with:
          registry-url: 'https://npm.pkg.github.com'

Package veröffentlichen

Nachdem wir Yarn konfiguriert haben, können wir als nächstes das NPM Package veröffentlichen. Für den Zugriff auf den Package Feed müssen wir Yarn ein Access Token mitgeben. Da bei GitHub aber wie bereits erwähnt alles integriert ist, müssen wir dieses Token nirgends hinterlegen. Es steht dem Workflow bereits zur Verfügung und wir können es direkt verwenden:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
name: build-and-publish

on: push

jobs:
  build-and-publish:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout Code
        uses: actions/[email protected]
      - name: Restore Node Modules
        uses: actions/[email protected]
        with:
          path: node_modules
          key: ${{ runner.os }}-modules-${{ hashFiles('**/yarn.lock') }}
          restore-keys: |
                        ${{ runner.os }}-modules-
      - name: Install Dependencies
        run: yarn --frozen-lockfile
      - name: Build Package
        run: yarn build
      - name: Prepare Publish
        uses: actions/[email protected]
        with:
          registry-url: 'https://npm.pkg.github.com'
      - name: Publish Package
        run: yarn publish dist/package
        env:
          NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

GitHub unterstützt nur Scoped Packages

Damit haben wir eigentlich alles beisammen, was wir für die Veröffentlichung des Packages benötigen. Der GitHub Actions Workflow schlägt jedoch fehl:

Unser Workflow schlägt mit einer nichts-sagenden Fehlermeldung fehl.

Die Fehlermeldung ist nicht wirklich hilfreich. Angeblich wurde kein Token gefunden, obwohl wir eines angegeben haben. Das Problem liegt aber wo anders, und zwar bei unserem Package Namen. Momentan heisst unser Package test-github-npm-feed. Dies ist ein ganz normaller und valider Name für ein NPM Package. Laut der GitHub Dokumentation unterstützt der GitHub NPM Feed jedoch nur Scoped Packages. Das sind Packages, die ein Scope Prefix haben. Das Scope Prefix definiert sozusagen, wer der Besitzer des Packages ist. Wir müssen unser package.json also noch anpassen und unseren GitHub Benutzernamen als Scope hinzufügen. In meinem Fall sieht dass dann so aus:

1
2
3
4
{
  "name": "@raeffs/test-github-npm-feed",
  "version": "1.0.0"
}

Anschliessend läuft der Workflow ohne Fehler durch und unser Package wird veröffentlicht:

Nach der Konfiguration des Scope Prefixes läuft der Workflow fehlerfrei durch und veröffentlicht unser Package.

Mehrere Packages pro Repository

Neben dem Scope Prefix gibt es noch eine weitere Voraussetzung, dass die Veröffentlichung des Packages auf dem GitHub Feed funktioniert: Das Package muss gleich heissen wie das Repository, in dem der GitHub Actions Workflow läuft. Dies war bereits der Fall und deshalb hat die Veröffentlichung auch funktioniert. Doch was, wenn wir mehrere unterschiedliche Packages in unserem Repository haben und diese veröffentlichen möchten?

Damit dies klappt, müssen wir im package.json neben dem Namen und der Version zusätzlich noch die URL des Repositories angeben:

1
2
3
4
5
{
  "name": "@raeffs/my-package",
  "repository": "https://github.com/raeffs/test-github-npm-feed",
  "version": "1.0.0"
}

Dann klappt die Veröffentlichung auch wenn der Package Name nicht mit dem Repository Namen übereinstimmt.