Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions celements-admin-frontend/.npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
@celements:registry=https://forge.celhosting.ch/api/packages/celements/npm/
//forge.celhosting.ch/api/packages/celements/npm/:_authToken=${FORGE_TOKEN}
89 changes: 89 additions & 0 deletions celements-admin-frontend/Jenkinsfile.npm

@msladek msladek Aug 10, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Jenkinsfile.npm and publish-forge-package.sh contain mostly reusable npm-specific publication workflow logic. Please move this into a focused npmPackagePipeline in the server-tools Jenkins shared library, analogous to dockerImagePipeline, and keep this repository’s pipeline declarative:

 @Library('synventis') _

 npmPackagePipeline(appDir: 'celements-admin-frontend')

Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
pipeline {
agent { label 'docker' }
options {
disableConcurrentBuilds()
skipDefaultCheckout(true)
timestamps()
}
parameters {
string(
name: 'GIT_REF',
defaultValue: '',
description: 'optional git ref to publish, e.g. commit SHA, branch name, or refs/tags/admin-frontend-v0.1.0'
)
}
stages {
stage('Checkout') {
steps {
script {
deleteDir()
String gitRef = params.GIT_REF?.trim()
def scmVars = checkout(!gitRef ? scm : [
$class: 'GitSCM',
branches: [[name: gitRef]],
userRemoteConfigs: scm.userRemoteConfigs
])
env.GIT_REV = scmVars.GIT_COMMIT ?: GIT_COMMIT
if (!GIT_REV) error('GIT_COMMIT missing after checkout')
env.GIT_SHA = GIT_REV.take(7)
}
}
}
stage('Prepare Package') {
steps {
dir('celements-admin-frontend') {
script {
String declaredVersion = sh(
script: 'node -p "require(\'./package.json\').version"',
returnStdout: true
).trim()
boolean snapshot = declaredVersion.toUpperCase().endsWith('-SNAPSHOT')
if (snapshot) {
String baseVersion = declaredVersion.substring(
0,
declaredVersion.length() - '-SNAPSHOT'.length()
)
env.PACKAGE_VERSION =
"${baseVersion}-snapshot.${env.BUILD_NUMBER}.${env.GIT_SHA}"
env.NPM_DIST_TAG = 'snapshot'
} else {
String expectedTag = "admin-frontend-v${declaredVersion}"
String releaseTag = sh(
script: "git tag --points-at HEAD --list '${expectedTag}'",
returnStdout: true
).trim()
if (releaseTag != expectedTag) {
error("release package requires tag ${expectedTag} at HEAD")
}
env.PACKAGE_VERSION = declaredVersion
env.NPM_DIST_TAG = 'latest'
}
sh "npm version --no-git-tag-version '${env.PACKAGE_VERSION}'"
}
}
}
}
stage('Build and Verify') {
steps {
dir('celements-admin-frontend') {
sh 'npm ci'
sh 'npm run build'
sh 'npm pack --dry-run --json'
}
}
}
stage('Publish Package') {
steps {
dir('celements-admin-frontend') {
withCredentials([usernamePassword(
credentialsId: 'forge-credentials',
usernameVariable: 'FORGE_USER',
passwordVariable: 'FORGE_TOKEN'
)]) {
sh './scripts/publish-forge-package.sh'
}
}
}
}
}
}
107 changes: 93 additions & 14 deletions celements-admin-frontend/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,19 +61,21 @@ The container generates `config.js` from its runtime environment. `.env.local` i

## 📜 Scripts

| Command | Description |
| -------------------- | ----------------------------------------------------------------- |
| `npm run dev` | Start dev server with concurrent type-checking and test watching |
| `npm run build` | Type-check + run tests + build production bundle |
| `npm run preview` | Preview the production build locally |
| `npm run test` | Run unit tests once with Vitest |
| `npm run test-watch` | Run unit tests in watch mode |
| `npm run type-check` | Type-check `.vue` files with `vue-tsc` |
| `npm run lint` | Lint source files with ESLint |
| `npm run lint-fix` | Auto-fix ESLint issues |
| `npm run format` | Check code formatting with Prettier |
| `npm run format-fix` | Auto-fix code formatting with Prettier |
| `npm run pre-commit` | Run type-check, format check and lint (recommended as a git hook) |
| Command | Description |
| ----------------------- | ----------------------------------------------------------------- |
| `npm run dev` | Start dev server with concurrent type-checking and test watching |
| `npm run build` | Type-check + run tests + build production bundle |
| `npm run preview` | Preview the production build locally |
| `npm run test` | Run unit tests once with Vitest |
| `npm run test-watch` | Run unit tests in watch mode |
| `npm run type-check` | Type-check `.vue` files with `vue-tsc` |
| `npm run lint` | Lint source files with ESLint |
| `npm run lint-fix` | Auto-fix ESLint issues |
| `npm run format` | Check code formatting with Prettier |
| `npm run format-fix` | Auto-fix code formatting with Prettier |
| `npm run pre-commit` | Run type-check, format check and lint (recommended as a git hook) |
| `npm run publish:check` | Build and inspect the package without publishing |
| `npm run publish:forge` | Publish an immutable version to the Forge npm registry |

---

Expand All @@ -97,7 +99,84 @@ src/
└── main.ts # Standalone development entry
```

The app exposes a `createCelementsAdminApp()` factory function from `bootstrap.ts` that allows the frontend to be embedded in a host application or run standalone.
The SPA bootstrap owns Vue Router. Shared feature components and islands use the router-independent
application runtime, which creates a separate Pinia instance for every mounted Vue application.

## Public package API

The supported `@celements/admin-frontend` entry points are:

- `@celements/admin-frontend/runtime` for `createCelementsApplication()`.
- `@celements/admin-frontend/page-attachments` for the router-free `PageAttachments` component and
its typed props, selection event, and `attachment-actions` slot contract.
- `@celements/admin-frontend/page-attachments-island` for the guarded
`<cel-page-attachments>` custom-element registration.
- `@celements/admin-frontend/styles.css` for downstream Vue applications using the feature component.

The custom element accepts `space-name`, `doc-name`, `locale`, and `local-dev`. It dispatches the
bubbling `attachment-selection-change` event. Framework packages are peer dependencies so a
downstream application supplies one Vue, Pinia, PrimeVue, vue-i18n, and VueFinder runtime. Vue
Router remains a development dependency for the deployable SPA shell and is not required by the
published feature, island, or runtime entry points.

`styles.css` is the complete supported stylesheet. It includes the scoped Celements
application/Tailwind styles and the processed VueFinder styles; consumers must not import CSS from
VueFinder or an internal package path separately.

## Forge npm package

The `@celements` scope is hosted at:

```text
https://forge.celhosting.ch/api/packages/celements/npm/
```

The checked-in `.npmrc` maps only the `@celements` scope to Forge. It reads `FORGE_TOKEN`, which
must be an access token with package read access for consumers and package write access for
publication. The npm Jenkins job uses the established `forge-credentials` username/password
credential and exposes its password as `FORGE_TOKEN`.

Install an immutable package version and its required peers (the example pins release `0.1.0`):

```sh
export FORGE_TOKEN='<package-read-token>'
npm install --save-exact @celements/admin-frontend@0.1.0 \
vue@^3.5.30 pinia@^3.0.4 primevue@^4.5.4 vue-i18n@^11.3.0 vuefinder@^4.1.1
```

Use only declared package exports:

```ts
import { PageAttachments } from '@celements/admin-frontend/page-attachments';
import { registerPageAttachmentsElement } from '@celements/admin-frontend/page-attachments-island';
import { createCelementsApplication } from '@celements/admin-frontend/runtime';
import '@celements/admin-frontend/styles.css';
```

`Jenkinsfile` continues to publish only the Docker image. Package publication is a separate Jenkins
job using `Jenkinsfile.npm`:

- a declared `x.y.z-SNAPSHOT` version becomes the unique immutable
`x.y.z-snapshot.<BUILD_NUMBER>.<GIT_SHA>` version and receives the `snapshot` dist-tag;
- a declared release version `x.y.z` must be built from the exact `admin-frontend-vx.y.z` Git tag
and receives the `latest` dist-tag;
- publication checks Forge first and refuses to overwrite an existing package version.

Repository code cannot create that Jenkins job. CI operations must configure a job whose script
path is `celements-admin-frontend/Jenkinsfile.npm`; until that infrastructure step exists, the
repository has a verifiable publication path but no package is published automatically.

Build and inspect the publishable package locally without credentials or publication:

```sh
npm run publish:check
```

From a clean, correctly versioned release checkout, the release job ultimately runs:

```sh
FORGE_TOKEN='<package-write-token>' NPM_DIST_TAG=latest npm run publish:forge
```

---

Expand Down
Loading