Compare commits

...
4 Commits
Author SHA1 Message Date
qwqcode d25beede91 fix(scripts): detect unpublished npm versions
Query the npm registry for the exact local version instead of comparing only with the latest dist-tag.

Report unpublished packages accurately and avoid spawning pnpm for each lookup.
2026-07-25 04:01:48 +08:00
qwqcode 16b690c8c0 fix(plugins): prepare unpublished packages for release
Declare compatible Artalk ranges and npm repository metadata for the pending plugin versions.

Correct ESM and CommonJS declaration exports, trim plugin-kit package contents, and validate package types in CI.
2026-07-25 04:01:43 +08:00
qwqcode 2036a0261e fix(ci): publish npm package with trusted OIDC
Replace the long-lived npm token with GitHub OIDC credentials, verify the release version, and retain pnpm packaging so the published tarball still includes the repository license.
2026-07-25 03:26:34 +08:00
qwqcode 66c8f6b94b fix(docker): push release images by digest
Avoid assigning temporary platform tags to digest-only outputs so Buildx can push canonical per-platform digests before the manifest merge.
2026-07-25 03:26:33 +08:00
17 changed files with 198 additions and 72 deletions

No files matched your search

-1
View File
@@ -165,7 +165,6 @@ jobs:
file: ./Dockerfile
platforms: ${{ matrix.platform }}
labels: ${{ steps.meta.outputs.labels }}
tags: ${{ steps.meta.outputs.tags }}
build-args: |
APP_VERSION=${{ inputs.version }}
APP_COMMIT_HASH=${{ inputs.commit }}
+22 -9
View File
@@ -12,9 +12,6 @@ on:
dry_run:
required: true
type: boolean
secrets:
NPM_TOKEN:
required: false
permissions:
contents: read
@@ -39,8 +36,7 @@ jobs:
- name: Setup node
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: 22.x
registry-url: https://registry.npmjs.org/
node-version: 24.x
package-manager-cache: false
- name: Install dependencies
@@ -49,15 +45,32 @@ jobs:
- name: Build
run: pnpm build
- name: Pack npm package
env:
VERSION: ${{ inputs.version }}
run: |
package_version="v$(node --print "require('./ui/artalk/package.json').version")"
if [[ "$package_version" != "$VERSION" ]]; then
echo "Package version $package_version does not match release version $VERSION."
exit 1
fi
pnpm --filter artalk pack --pack-destination "$RUNNER_TEMP"
package_file="$RUNNER_TEMP/artalk-${package_version#v}.tgz"
if [[ ! -f "$package_file" ]]; then
echo "Expected package tarball was not created: $package_file"
exit 1
fi
echo "PACKAGE_FILE=$package_file" >> "$GITHUB_ENV"
- name: Validate package
if: ${{ inputs.dry_run }}
run: pnpm publish -F artalk --no-git-checks --dry-run
run: npm publish "$PACKAGE_FILE" --dry-run --access public
- name: Publish
if: ${{ !inputs.dry_run }}
run: pnpm publish -F artalk --no-git-checks --provenance --access public
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
# npm validates build.yml, the calling workflow, for trusted publishing.
run: npm publish "$PACKAGE_FILE" --access public
# GitHub Packages
#
-2
View File
@@ -75,8 +75,6 @@ jobs:
permissions:
contents: read
id-token: write
secrets:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
# Build the Docker image
docker:
+8
View File
@@ -70,6 +70,14 @@ jobs:
if: ${{ matrix.quality }}
run: pnpm build:plugins
- name: Plugins Type Test
if: ${{ matrix.quality }}
run: pnpm test:plugins
- name: Plugins Package Test
if: ${{ matrix.quality }}
run: pnpm test:plugins:attw
- name: Lint
if: ${{ matrix.quality }}
run: pnpm eslint ui && pnpm prettier --check ui
+2
View File
@@ -40,6 +40,8 @@ export default defineConfig(
'**/*.config.js',
'**/*.config.ts',
'**/*.d.ts',
'**/*.d.cts',
'**/*.d.mts',
],
},
+2
View File
@@ -19,6 +19,8 @@
"build:auth": "pnpm -F @artalk/plugin-auth build",
"build:plugin-kit": "pnpm -F @artalk/plugin-kit build",
"build:eslint-plugin": "pnpm -F eslint-plugin-artalk build",
"test:plugins": "pnpm -F @artalk/plugin-* exec tsc --noEmit && pnpm -F eslint-plugin-artalk exec tsc --noEmit && pnpm -F eslint-plugin-artalk test --run",
"test:plugins:attw": "pnpm -F @artalk/plugin-* -F eslint-plugin-artalk test:attw",
"build:typedoc": "pnpm typedoc",
"build:docs": "pnpm build && pnpm build:plugins && pnpm -F=docs-landing build && pnpm -F=docs-swagger swagger:build && pnpm -F=docs build:docs && pnpm after:build-docs",
"after:build-docs": "cp -rf docs/landing/dist/* docs/swagger/dist/* docs/docs/.vitepress/dist && pnpm build:typedoc && pnpm fetch:registry",
+50 -32
View File
@@ -1,15 +1,34 @@
#!/usr/bin/env node
import { promises as fs, readFileSync } from 'fs'
import path from 'path'
import { execFileSync } from 'child_process'
import process from 'process'
import { promises as fs, readFileSync } from 'node:fs'
import path from 'node:path'
import process from 'node:process'
import { fileURLToPath } from 'node:url'
const __dirname = path.dirname(new URL(import.meta.url).pathname)
const __dirname = path.dirname(fileURLToPath(import.meta.url))
const REGISTRY_URL = 'https://registry.npmjs.org/'
const REQUEST_TIMEOUT = 10_000
// Helper to run shell commands
const getPublishedVersion = (packageName) =>
execFileSync('pnpm', ['info', packageName, 'version'], { encoding: 'utf-8' }).trim()
const getPublishedPackage = async (packageName) => {
const packageUrl = new URL(encodeURIComponent(packageName), REGISTRY_URL)
const response = await fetch(packageUrl, {
headers: { Accept: 'application/vnd.npm.install-v1+json' },
signal: AbortSignal.timeout(REQUEST_TIMEOUT),
})
if (response.status === 404) {
return { latestVersion: null, versions: new Set() }
}
if (!response.ok) {
throw new Error(`npm registry returned HTTP ${response.status} for ${packageName}`)
}
const packageMetadata = await response.json()
return {
latestVersion: packageMetadata['dist-tags']?.latest || null,
versions: new Set(Object.keys(packageMetadata.versions || {})),
}
}
// Recursively find all directories containing package.json
const findNodeProjects = async (dir) => {
@@ -36,8 +55,8 @@ const findNodeProjects = async (dir) => {
return projects
}
// Compare local version with the latest npm version
const checkVersionMismatch = async (projectPath) => {
// Check whether the exact local version has already been published to npm.
const checkVersionPublished = async (projectPath) => {
const packageJsonPath = path.join(projectPath, 'package.json')
try {
@@ -46,23 +65,22 @@ const checkVersionMismatch = async (projectPath) => {
const localVersion = packageJson.version
const packageName = packageJson.name
// Get the latest version from npm using pnpm info
const npmVersion = getPublishedVersion(packageName)
const { latestVersion, versions } = await getPublishedPackage(packageName)
if (localVersion === npmVersion) {
console.log(`✅ ${packageName} is up to date (${npmVersion})`)
} else {
console.log(`❌ ${packageName} is outdated (local: ${localVersion}, npm: ${npmVersion})`)
if (versions.has(localVersion)) {
const latestSuffix =
localVersion === latestVersion ? '' : `; npm latest is ${latestVersion || 'unset'}`
console.log(`✅ ${packageName}@${localVersion} is published${latestSuffix}`)
return null
}
if (localVersion !== npmVersion) {
return { packageName, localVersion, latestVersion: npmVersion }
}
console.log(
`❌ ${packageName}@${localVersion} is not published (npm latest: ${latestVersion || 'none'})`,
)
return { packageName, localVersion, latestVersion }
} catch (error) {
throw new Error(`Failed to check npm version for ${projectPath}`, { cause: error })
}
return null
}
// Parse command-line arguments to get the project name if provided
@@ -79,8 +97,8 @@ const getArgs = () => {
return specifiedProject
}
// Main function to find outdated packages
const findOutdatedProjects = async () => {
// Main function to find unpublished local package versions
const findUnpublishedProjects = async () => {
const specifiedProject = getArgs()
let projects = await findNodeProjects(path.join(__dirname, '../ui'))
console.log(`Found ${projects.length} projects under 'ui' directory.\n`)
@@ -100,29 +118,29 @@ const findOutdatedProjects = async () => {
}
}
const outdatedProjects = []
const unpublishedProjects = []
for (const project of projects) {
const result = await checkVersionMismatch(project)
const result = await checkVersionPublished(project)
if (result) {
outdatedProjects.push(result)
unpublishedProjects.push(result)
}
}
console.log('\n==================================================\n')
if (outdatedProjects.length === 0) {
console.log('✅ All projects have the latest versions pushed to npm.')
if (unpublishedProjects.length === 0) {
console.log('✅ Every local package version is published to npm.')
} else {
console.log('Projects with outdated versions:\n')
outdatedProjects.forEach(({ packageName, localVersion, latestVersion }) => {
console.log(`❌ ${packageName}: Local version ${localVersion}, NPM version ${latestVersion}`)
console.log('Local package versions not published to npm:\n')
unpublishedProjects.forEach(({ packageName, localVersion, latestVersion }) => {
console.log(`❌ ${packageName}: local ${localVersion}, npm latest ${latestVersion || 'none'}`)
})
process.exit(1)
}
}
findOutdatedProjects().catch((error) => {
findUnpublishedProjects().catch((error) => {
console.error(error)
process.exitCode = 1
})
+8 -1
View File
@@ -4,6 +4,12 @@
"type": "module",
"license": "MIT",
"homepage": "https://github.com/ArtalkJS/Artalk/tree/master/ui/eslint-plugin-artalk",
"bugs": "https://github.com/ArtalkJS/Artalk/issues",
"repository": {
"type": "git",
"url": "git+https://github.com/ArtalkJS/Artalk.git",
"directory": "ui/eslint-plugin-artalk"
},
"files": [
"dist"
],
@@ -24,7 +30,8 @@
"scripts": {
"dev": "tsdown --watch",
"build": "tsdown",
"test": "vitest"
"test": "vitest",
"test:attw": "pnpx @arethetypeswrong/cli@0.18.5 --pack ."
},
"dependencies": {
"@typescript-eslint/utils": "^8.65.0"
+22 -5
View File
@@ -1,8 +1,19 @@
{
"name": "@artalk/plugin-auth",
"version": "0.0.3",
"minAppVersion": "2.10.0",
"license": "LGPL-3.0-only",
"description": "Auth plugin for artalk",
"homepage": "https://github.com/ArtalkJS/Artalk/tree/master/ui/plugin-auth",
"bugs": "https://github.com/ArtalkJS/Artalk/issues",
"repository": {
"type": "git",
"url": "git+https://github.com/ArtalkJS/Artalk.git",
"directory": "ui/plugin-auth"
},
"publishConfig": {
"access": "public"
},
"type": "module",
"files": [
"dist"
@@ -12,15 +23,21 @@
"types": "./dist/artalk-plugin-auth.d.ts",
"exports": {
".": {
"types": "./dist/artalk-plugin-auth.d.ts",
"import": "./dist/artalk-plugin-auth.mjs",
"require": "./dist/artalk-plugin-auth.cjs"
"import": {
"types": "./dist/artalk-plugin-auth.d.ts",
"default": "./dist/artalk-plugin-auth.mjs"
},
"require": {
"types": "./dist/artalk-plugin-auth.d.cts",
"default": "./dist/artalk-plugin-auth.cjs"
}
}
},
"scripts": {
"dev": "vite",
"build": "vite build",
"serve": "vite preview"
"serve": "vite preview",
"test:attw": "pnpx @arethetypeswrong/cli@0.18.5 --pack ."
},
"devDependencies": {
"@artalk/plugin-kit": "workspace:^",
@@ -30,6 +47,6 @@
"vite-plugin-solid": "^2.11.13"
},
"peerDependencies": {
"artalk": "workspace:^"
"artalk": "^2.10.0"
}
}
@@ -1,3 +0,0 @@
import type { ArtalkPlugin } from 'artalk'
declare const ArtalkAuthPlugin: ArtalkPlugin
-3
View File
@@ -1,3 +0,0 @@
import type { ArtalkPlugin } from 'artalk'
declare const ArtalkAuthPlugin: ArtalkPlugin
+22 -5
View File
@@ -1,8 +1,19 @@
{
"name": "@artalk/plugin-katex",
"version": "0.2.5",
"minAppVersion": "2.8.6",
"license": "MIT",
"description": "The katex plugin for artalk",
"homepage": "https://github.com/ArtalkJS/Artalk/tree/master/ui/plugin-katex",
"bugs": "https://github.com/ArtalkJS/Artalk/issues",
"repository": {
"type": "git",
"url": "git+https://github.com/ArtalkJS/Artalk.git",
"directory": "ui/plugin-katex"
},
"publishConfig": {
"access": "public"
},
"type": "module",
"main": "./dist/artalk-plugin-katex.js",
"module": "./dist/artalk-plugin-katex.mjs",
@@ -13,7 +24,8 @@
"scripts": {
"dev": "vite",
"build": "vite build",
"serve": "vite preview"
"serve": "vite preview",
"test:attw": "pnpx @arethetypeswrong/cli@0.18.5 --pack ."
},
"devDependencies": {
"@artalk/plugin-kit": "workspace:^",
@@ -22,14 +34,19 @@
"marked": "^18.0.7"
},
"peerDependencies": {
"artalk": "workspace:^",
"artalk": ">=2.8.6 <3.0.0",
"katex": ">=0.16.45 <0.18.0"
},
"exports": {
".": {
"types": "./dist/artalk-plugin-katex.d.ts",
"import": "./dist/artalk-plugin-katex.mjs",
"require": "./dist/artalk-plugin-katex.cjs"
"import": {
"types": "./dist/artalk-plugin-katex.d.ts",
"default": "./dist/artalk-plugin-katex.mjs"
},
"require": {
"types": "./dist/artalk-plugin-katex.d.cts",
"default": "./dist/artalk-plugin-katex.cjs"
}
}
}
}
+9
View File
@@ -0,0 +1,9 @@
import Artalk from 'artalk'
export {}
declare global {
interface Window {
Artalk?: typeof Artalk
}
}
+1 -2
View File
@@ -1,10 +1,9 @@
import Artalk, { ArtalkPlugin } from 'artalk'
import Artalk from 'artalk'
export {}
declare global {
interface Window {
Artalk?: typeof Artalk
ArtalkPlugins?: { [name: string]: ArtalkPlugin }
}
}
+30 -3
View File
@@ -2,6 +2,16 @@
"name": "@artalk/plugin-kit",
"version": "1.0.9",
"description": "The plugin kit for Artalk",
"homepage": "https://github.com/ArtalkJS/Artalk/tree/master/ui/plugin-kit",
"bugs": "https://github.com/ArtalkJS/Artalk/issues",
"repository": {
"type": "git",
"url": "git+https://github.com/ArtalkJS/Artalk.git",
"directory": "ui/plugin-kit"
},
"publishConfig": {
"access": "public"
},
"type": "module",
"main": "dist/main.cjs",
"types": "dist/main.d.cts",
@@ -16,15 +26,32 @@
"default": "./dist/main.mjs"
}
},
"./client": "./client.d.ts"
"./client": {
"import": {
"types": "./client.d.ts"
},
"require": {
"types": "./client.d.cts"
}
}
},
"scripts": {
"dev": "tsdown --watch",
"build": "tsdown"
"build": "tsdown",
"test:attw": "pnpx @arethetypeswrong/cli@0.18.5 --pack ."
},
"keywords": [],
"author": "",
"license": "MIT",
"files": [
"dist/**/*.cjs",
"dist/**/*.mjs",
"dist/**/*.map",
"dist/**/*.d.cts",
"dist/**/*.d.mts",
"client.d.ts",
"client.d.cts"
],
"dependencies": {
"@microsoft/api-extractor": "^7.58.12",
"picocolors": "^1.1.1"
@@ -35,7 +62,7 @@
"vite": "^8.1.5"
},
"peerDependencies": {
"artalk": "workspace:^",
"artalk": "^2.10.0",
"typescript": "*",
"vite": "*"
}
-1
View File
@@ -1,5 +1,4 @@
interface Window {
lightGallery: (el: any, options: any) => any | undefined
Fancybox?: any
lightbox?: any
PhotoSwipeLightbox?: any
+22 -5
View File
@@ -1,8 +1,19 @@
{
"name": "@artalk/plugin-lightbox",
"version": "0.2.5",
"minAppVersion": "2.9.1",
"license": "MIT",
"description": "The image lightbox plugin for artalk",
"homepage": "https://github.com/ArtalkJS/Artalk/tree/master/ui/plugin-lightbox",
"bugs": "https://github.com/ArtalkJS/Artalk/issues",
"repository": {
"type": "git",
"url": "git+https://github.com/ArtalkJS/Artalk.git",
"directory": "ui/plugin-lightbox"
},
"publishConfig": {
"access": "public"
},
"type": "module",
"main": "./dist/artalk-plugin-lightbox.js",
"module": "./dist/artalk-plugin-lightbox.mjs",
@@ -13,7 +24,8 @@
"scripts": {
"dev": "vite",
"build": "vite build",
"serve": "vite preview"
"serve": "vite preview",
"test:attw": "pnpx @arethetypeswrong/cli@0.18.5 --pack ."
},
"devDependencies": {
"@artalk/plugin-kit": "workspace:^",
@@ -24,7 +36,7 @@
"photoswipe": "^5.4.4"
},
"peerDependencies": {
"artalk": "workspace:^",
"artalk": "^2.9.1",
"fancybox": "^3.0.1",
"lightbox2": "^2.12.0",
"lightgallery": "^2.9.0",
@@ -46,9 +58,14 @@
},
"exports": {
".": {
"types": "./dist/artalk-plugin-lightbox.d.ts",
"import": "./dist/artalk-plugin-lightbox.mjs",
"require": "./dist/artalk-plugin-lightbox.cjs"
"import": {
"types": "./dist/artalk-plugin-lightbox.d.ts",
"default": "./dist/artalk-plugin-lightbox.mjs"
},
"require": {
"types": "./dist/artalk-plugin-lightbox.d.cts",
"default": "./dist/artalk-plugin-lightbox.cjs"
}
}
}
}