diff --git a/.github/workflows/publish.yml b/.github/workflows/docs.yml similarity index 100% rename from .github/workflows/publish.yml rename to .github/workflows/docs.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 000000000..b3fe446b9 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,61 @@ +# Unified workflow for both prerelease and stable releases +# Prerelease: triggered on push to main +# Stable: triggered on push of tag matching 'v*.*.*' +# NOTE: The reason this is all done in one workflow is because npmjs.com only +# allows one GH Actions file to be setup as a trusted publisher. +name: Publish npm package + +on: + push: + branches: + - main + tags: + - 'v[0-9]+.[0-9]+.[0-9]+' + +permissions: + id-token: write + +jobs: + publish: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - uses: actions/setup-node@v4 + with: + node-version: current + registry-url: 'https://registry.npmjs.org/' + + - name: Install dependencies + run: npm ci + + - name: Determine version and tag + id: version + run: | + if [[ "${{ github.ref }}" == refs/tags/* ]]; then + # Stable release: use tag version + VERSION=${GITHUB_REF#refs/tags/v} + npm version "$VERSION" --no-git-tag-version + TAG="latest" + echo "version=$VERSION" >> $GITHUB_OUTPUT + echo "tag=$TAG" >> $GITHUB_OUTPUT + echo "Setting version to $VERSION for stable release" + else + # Prerelease: bump from latest stable tag + LATEST_TAG=$(git tag --list "v*.*.*" --sort=-v:refname | grep -v '-' | head -n1) + npm version "${LATEST_TAG#v}" --no-git-tag-version + npm version prerelease --preid="dev.${{ github.sha }}" --no-git-tag-version + VERSION=$(node -p "require('./package.json').version") + TAG="dev" + echo "version=$VERSION" >> $GITHUB_OUTPUT + echo "tag=$TAG" >> $GITHUB_OUTPUT + echo "Setting version to $VERSION for prerelease" + fi + + - name: Build with remote save enabled + run: VITE_REMOTE_SERVER_URL= VITE_ENABLE_REMOTE_SAVE=true npm run build + + - name: Publish to npm + run: npm publish --tag ${{ steps.version.outputs.tag }} --access public --provenance diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f8f37f59f..e5021db58 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,7 +4,7 @@ Before you begin, make sure your environment matches the following versions: -- **Node.js**: >= 18.20.0 (20.x LTS recommended) +- **Node.js**: >= 18.20.0 (20.x LTS recommended) - **npm**: >= 9.x (npm 10+ works with Node 20) Check your versions: @@ -97,6 +97,27 @@ npm run test:e2e:dev -- -- --spec ./tests/specs/remote-manifest.e2e.ts --- +## Versioning + +Merging to `main` automatically publishes prerelease packages to NPM: + +1. Merge creates a prerelease tag (e.g. v4.4.0-dev.) +2. Tag triggers automatic NPM publish with `@dev` dist-tag + +```bash +npm install volview # Gets latest stable release (e.g., 4.4.0) +npm install volview@dev # Gets latest dev package (most recent commit to main branch) +``` + +To publish a new release via CI/CD, manually create and push a tag, e.g.: + +```bash +git tag v4.5.0 # must match format 'v[0-9]+.[0-9]+.[0-9]+' +git push origin --tags +``` + +--- + ## Developing with VTK.js Follow these steps to develop against a custom development branch of VTK.js: diff --git a/package-lock.json b/package-lock.json index a48ee77c9..ecc22d325 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,13 +1,12 @@ { "name": "volview", - "version": "4.4.0", + "version": "0.0.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "volview", - "version": "4.4.0", - "hasInstallScript": true, + "version": "0.0.0", "dependencies": { "@aws-sdk/client-s3": "^3.435.0", "@itk-wasm/dicom": "7.6.0", diff --git a/package.json b/package.json index 56d14665d..b0f6b4c88 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,11 @@ { "name": "volview", - "version": "4.4.0", + "version": "0.0.0", "type": "module", + "files": [ + "dist", + "src" + ], "scripts": { "dev": "cross-env VITE_SHOW_SAMPLE_DATA=true vite", "preview": "vite preview", @@ -19,7 +23,6 @@ "build:resample": "itk-wasm -s src/io/resample/ build ", "build:resample:debug": "itk-wasm -s src/io/resample/ build -- -DCMAKE_BUILD_TYPE=Debug", "build:watch": "npm run build -- --watch", - "postinstall": "patch-package", "prettify": "prettier --write src tests", "docs:dev": "vitepress dev docs", "docs:build": "vitepress build docs", diff --git a/vite.config.ts b/vite.config.ts index b136377e4..2c1acd154 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,5 +1,6 @@ /// import * as path from 'node:path'; +import * as fs from 'node:fs'; import { createRequire } from 'node:module'; import { Plugin, defineConfig, normalizePath } from 'vite'; import vue from '@vitejs/plugin-vue'; @@ -10,35 +11,8 @@ import { visualizer } from 'rollup-plugin-visualizer'; import { sentryVitePlugin } from '@sentry/vite-plugin'; import replace from '@rollup/plugin-replace'; -import pkgLock from './package-lock.json'; import { config } from './wdio.shared.conf'; -function getPackageInfo(lockInfo: typeof pkgLock) { - if (lockInfo.lockfileVersion === 2) { - return { - versions: { - volview: lockInfo.version, - 'vtk.js': lockInfo.dependencies['@kitware/vtk.js'].version, - 'itk-wasm': lockInfo.dependencies['itk-wasm'].version, - }, - }; - } - - if (lockInfo.lockfileVersion === 3) { - return { - versions: { - volview: lockInfo.version, - 'vtk.js': lockInfo.packages['node_modules/@kitware/vtk.js'].version, - 'itk-wasm': lockInfo.packages['node_modules/itk-wasm'].version, - }, - }; - } - - throw new Error( - 'VolView build: your package-lock.json version is not 2 or 3. Cannot extract dependency versions.' - ); -} - function resolveNodeModulePath(moduleName: string) { const require = createRequire(import.meta.url); let modulePath = normalizePath(require.resolve(moduleName)); @@ -55,13 +29,32 @@ function resolvePath(...args: string[]) { return normalizePath(path.resolve(...args)); } +function getPackageInfo() { + const mainPkgPath = path.resolve(__dirname, 'package.json'); + const mainPkg = JSON.parse(fs.readFileSync(mainPkgPath, 'utf-8')); + + const vtkJsPath = path.join(resolveNodeModulePath('@kitware/vtk.js'), 'package.json'); + const vtkJsPkg = JSON.parse(fs.readFileSync(vtkJsPath, 'utf-8')); + + const itkWasmPath = path.join(resolveNodeModulePath('itk-wasm'), 'package.json'); + const itkWasmPkg = JSON.parse(fs.readFileSync(itkWasmPath, 'utf-8')); + + return { + versions: { + volview: mainPkg.version, + 'vtk.js': vtkJsPkg.version, + 'itk-wasm': itkWasmPkg.version, + }, + }; +} + const rootDir = resolvePath(__dirname); const distDir = resolvePath(rootDir, 'dist'); const { ANALYZE_BUNDLE, SENTRY_AUTH_TOKEN, SENTRY_ORG, SENTRY_PROJECT } = process.env; -const pkgInfo = getPackageInfo(pkgLock); +const pkgInfo = getPackageInfo(); function configureSentryPlugin() { return SENTRY_AUTH_TOKEN && SENTRY_ORG && SENTRY_PROJECT