Merge branch 'ci' into 'master'
Add CI/CD See merge request riftenlabs/lib/wizardconnect!1
This commit is contained in:
commit
8c02f72eee
8 changed files with 494 additions and 12 deletions
97
.gitlab-ci.yml
Normal file
97
.gitlab-ci.yml
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
.node-common:
|
||||
image: node:trixie-slim
|
||||
variables:
|
||||
npm_config_cache: "$CI_PROJECT_DIR/.npm-cache"
|
||||
cache:
|
||||
key:
|
||||
files:
|
||||
- package-lock.json
|
||||
paths:
|
||||
- .npm-cache/
|
||||
before_script:
|
||||
- node --version
|
||||
|
||||
security_checker:
|
||||
extends: .node-common
|
||||
script:
|
||||
- npm audit --audit-level=moderate
|
||||
allow_failure: false
|
||||
|
||||
test:
|
||||
extends: .node-common
|
||||
script:
|
||||
- npm install
|
||||
- npm run test
|
||||
|
||||
build:
|
||||
extends: .node-common
|
||||
script:
|
||||
- npm install
|
||||
- npm run build
|
||||
artifacts:
|
||||
paths:
|
||||
- packages/*/dist/
|
||||
- node_modules/
|
||||
expire_in: 1 hour
|
||||
|
||||
lint:
|
||||
extends: .node-common
|
||||
script:
|
||||
- npm install
|
||||
- npm run build # required by eslint import plugin
|
||||
- npm run lint
|
||||
|
||||
test-integration:
|
||||
extends: .node-common
|
||||
script:
|
||||
- npm install
|
||||
- npm run build
|
||||
- npm run test:integration --workspace=@wizardconnect/wallet
|
||||
|
||||
publish:
|
||||
extends: .node-common
|
||||
# npm trusted publishing via GitLab OIDC — no NPM_TOKEN needed.
|
||||
# Each @wizardconnect/* package must be linked to this GitLab project
|
||||
# on npmjs.com: package Settings > Publishing access > Trusted publishers.
|
||||
id_tokens:
|
||||
SIGSTORE_ID_TOKEN:
|
||||
aud: sigstore
|
||||
script:
|
||||
- apt-get update && apt-get install -y git
|
||||
- node contrib/auto-publish.js
|
||||
rules:
|
||||
- if: $CI_COMMIT_BRANCH == "master"
|
||||
needs:
|
||||
- test
|
||||
- lint
|
||||
|
||||
pages:
|
||||
image: python:3-slim
|
||||
cache:
|
||||
key: pages-pip
|
||||
paths:
|
||||
- .pip-cache/
|
||||
variables:
|
||||
PIP_CACHE_DIR: "$CI_PROJECT_DIR/.pip-cache"
|
||||
script:
|
||||
- pip install zensical
|
||||
- zensical build
|
||||
- mv site public
|
||||
artifacts:
|
||||
paths:
|
||||
- public
|
||||
rules:
|
||||
- if: $CI_COMMIT_BRANCH == "master"
|
||||
|
||||
force-publish:
|
||||
extends: .node-common
|
||||
id_tokens:
|
||||
SIGSTORE_ID_TOKEN:
|
||||
aud: sigstore
|
||||
script:
|
||||
- apt-get update && apt-get install -y git
|
||||
- node contrib/force-publish.js
|
||||
rules:
|
||||
- if: $CI_COMMIT_BRANCH == "master"
|
||||
when: manual
|
||||
allow_failure: false
|
||||
208
contrib/auto-publish.js
Normal file
208
contrib/auto-publish.js
Normal file
|
|
@ -0,0 +1,208 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Fully automated package publishing for monorepo
|
||||
* - Detects changes automatically
|
||||
* - Gets latest version from npm and bumps appropriately
|
||||
* - Skips private packages
|
||||
* - Uses npm trusted publishing (OIDC provenance) — no NPM_TOKEN needed
|
||||
* - No git commits, just publishes
|
||||
* - No manual interaction required
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { execSync } = require('child_process');
|
||||
|
||||
function discoverPackages() {
|
||||
const packagesDir = 'packages';
|
||||
const packagePaths = {};
|
||||
|
||||
const packageDirs = fs.readdirSync(packagesDir, { withFileTypes: true })
|
||||
.filter(dirent => dirent.isDirectory())
|
||||
.map(dirent => dirent.name);
|
||||
|
||||
for (const packageDir of packageDirs) {
|
||||
const packageJsonPath = path.join(packagesDir, packageDir, 'package.json');
|
||||
if (fs.existsSync(packageJsonPath)) {
|
||||
try {
|
||||
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
||||
if (packageJson.private) {
|
||||
log(`Skipping private package: ${packageJson.name || packageDir}`);
|
||||
continue;
|
||||
}
|
||||
if (packageJson.name) {
|
||||
packagePaths[packageJson.name] = path.join(packagesDir, packageDir);
|
||||
}
|
||||
} catch (error) {
|
||||
log(`Warning: Could not parse package.json in ${packageDir}: ${error.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return packagePaths;
|
||||
}
|
||||
|
||||
function log(message) {
|
||||
console.log(`[AUTO-PUBLISH] ${message}`);
|
||||
}
|
||||
|
||||
function execCommand(command, cwd = process.cwd()) {
|
||||
try {
|
||||
return execSync(command, { cwd, stdio: 'pipe', encoding: 'utf8' });
|
||||
} catch (error) {
|
||||
throw new Error(`Command failed: ${command}\n${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
function checkPackageChanges(packageName, packagePath) {
|
||||
log(`Checking changes for ${packageName} in ${packagePath}`);
|
||||
|
||||
const latestNpmVersion = getLatestVersionFromNpm(packageName);
|
||||
if (latestNpmVersion === null) {
|
||||
log(`New package ${packageName} detected - will be published`);
|
||||
return true;
|
||||
}
|
||||
|
||||
try {
|
||||
execCommand(`git diff --quiet HEAD~1 HEAD -- ${packagePath}/src/`);
|
||||
return false;
|
||||
} catch (error) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
function getLatestVersionFromNpm(packageName) {
|
||||
try {
|
||||
const result = execCommand(`npm view ${packageName} version`);
|
||||
return result.trim();
|
||||
} catch (error) {
|
||||
const errorMessage = error.message || error.toString();
|
||||
|
||||
if (errorMessage.includes('404')) {
|
||||
log(`Package ${packageName} not found on npm registry (404) - treating as new package`);
|
||||
return null;
|
||||
}
|
||||
|
||||
log(`ERROR: Failed to check npm registry for ${packageName}: ${errorMessage}`);
|
||||
throw new Error(`Failed to check npm registry for ${packageName}: ${errorMessage}`);
|
||||
}
|
||||
}
|
||||
|
||||
function getCurrentVersion(packagePath) {
|
||||
const packageJson = JSON.parse(fs.readFileSync(path.join(packagePath, 'package.json'), 'utf8'));
|
||||
return packageJson.version;
|
||||
}
|
||||
|
||||
function bumpVersion(currentVersion, latestNpmVersion) {
|
||||
const [major, minor, patch] = currentVersion.split('.').map(Number);
|
||||
|
||||
if (latestNpmVersion === null) {
|
||||
return currentVersion;
|
||||
}
|
||||
|
||||
const [npmMajor, npmMinor, npmPatch] = latestNpmVersion.split('.').map(Number);
|
||||
|
||||
if (currentVersion > latestNpmVersion) {
|
||||
return `${major}.${minor}.${patch + 1}`;
|
||||
}
|
||||
|
||||
if (latestNpmVersion > currentVersion) {
|
||||
return `${npmMajor}.${npmMinor}.${npmPatch + 1}`;
|
||||
}
|
||||
|
||||
return `${major}.${minor}.${patch + 1}`;
|
||||
}
|
||||
|
||||
function updatePackageVersion(packagePath, packageName, newVersion) {
|
||||
log(`Updating ${packageName} to version ${newVersion}`);
|
||||
|
||||
const packageJsonPath = path.join(packagePath, 'package.json');
|
||||
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
||||
|
||||
packageJson.version = newVersion;
|
||||
fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2) + '\n');
|
||||
|
||||
return newVersion;
|
||||
}
|
||||
|
||||
function publishPackage(packagePath, packageName) {
|
||||
log(`Publishing ${packageName}`);
|
||||
|
||||
try {
|
||||
execCommand('npm publish --provenance --access public', packagePath);
|
||||
log(`Successfully published ${packageName}`);
|
||||
} catch (error) {
|
||||
log(`Failed to publish ${packageName}: ${error.message}`);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
function main() {
|
||||
const isDryRun = process.argv.includes('--dry-run');
|
||||
|
||||
if (isDryRun) {
|
||||
log('DRY RUN MODE - No packages will be published');
|
||||
} else {
|
||||
log('Starting fully automated package publishing');
|
||||
}
|
||||
|
||||
log('Discovering packages...');
|
||||
const packagePaths = discoverPackages();
|
||||
log(`Found ${Object.keys(packagePaths).length} publishable packages: ${Object.keys(packagePaths).join(', ')}`);
|
||||
|
||||
log('Installing dependencies...');
|
||||
execCommand('npm ci');
|
||||
|
||||
log('Building all packages...');
|
||||
execCommand('npm run build');
|
||||
|
||||
const packagesToPublish = [];
|
||||
|
||||
for (const [packageName, packagePath] of Object.entries(packagePaths)) {
|
||||
if (checkPackageChanges(packageName, packagePath)) {
|
||||
log(`Changes detected for ${packageName}`);
|
||||
packagesToPublish.push(packageName);
|
||||
} else {
|
||||
log(`No changes detected for ${packageName}`);
|
||||
}
|
||||
}
|
||||
|
||||
if (packagesToPublish.length === 0) {
|
||||
log('No packages have changes, skipping publish');
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
log(`Found ${packagesToPublish.length} packages with changes: ${packagesToPublish.join(', ')}`);
|
||||
|
||||
for (const packageName of packagesToPublish) {
|
||||
const packagePath = packagePaths[packageName];
|
||||
const currentVersion = getCurrentVersion(packagePath);
|
||||
const latestNpmVersion = getLatestVersionFromNpm(packageName);
|
||||
|
||||
if (latestNpmVersion === null) {
|
||||
log(`Current version: ${currentVersion}, Latest on npm: (new package)`);
|
||||
} else {
|
||||
log(`Current version: ${currentVersion}, Latest on npm: ${latestNpmVersion}`);
|
||||
}
|
||||
|
||||
const newVersion = bumpVersion(currentVersion, latestNpmVersion);
|
||||
updatePackageVersion(packagePath, packageName, newVersion);
|
||||
|
||||
if (!isDryRun) {
|
||||
publishPackage(packagePath, packageName);
|
||||
} else {
|
||||
log(`[DRY RUN] Would publish ${packageName}@${newVersion}`);
|
||||
}
|
||||
}
|
||||
|
||||
if (isDryRun) {
|
||||
log('Dry run completed - no packages were actually published');
|
||||
} else {
|
||||
log('Automated publishing completed successfully');
|
||||
}
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
main();
|
||||
}
|
||||
177
contrib/force-publish.js
Normal file
177
contrib/force-publish.js
Normal file
|
|
@ -0,0 +1,177 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Force publish all packages in monorepo
|
||||
* - Builds everything
|
||||
* - Publishes everything regardless of changes
|
||||
* - Skips private packages
|
||||
* - Uses npm trusted publishing (OIDC provenance) — no NPM_TOKEN needed
|
||||
* - Gets latest version from npm and bumps appropriately
|
||||
* - No git commits, just publishes
|
||||
* - No manual interaction required
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { execSync } = require('child_process');
|
||||
|
||||
function discoverPackages() {
|
||||
const packagesDir = 'packages';
|
||||
const packagePaths = {};
|
||||
|
||||
const packageDirs = fs.readdirSync(packagesDir, { withFileTypes: true })
|
||||
.filter(dirent => dirent.isDirectory())
|
||||
.map(dirent => dirent.name);
|
||||
|
||||
for (const packageDir of packageDirs) {
|
||||
const packageJsonPath = path.join(packagesDir, packageDir, 'package.json');
|
||||
if (fs.existsSync(packageJsonPath)) {
|
||||
try {
|
||||
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
||||
if (packageJson.private) {
|
||||
log(`Skipping private package: ${packageJson.name || packageDir}`);
|
||||
continue;
|
||||
}
|
||||
if (packageJson.name) {
|
||||
packagePaths[packageJson.name] = path.join(packagesDir, packageDir);
|
||||
}
|
||||
} catch (error) {
|
||||
log(`Warning: Could not parse package.json in ${packageDir}: ${error.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return packagePaths;
|
||||
}
|
||||
|
||||
function log(message) {
|
||||
console.log(`[FORCE-PUBLISH] ${message}`);
|
||||
}
|
||||
|
||||
function execCommand(command, cwd = process.cwd()) {
|
||||
try {
|
||||
return execSync(command, { cwd, stdio: 'pipe', encoding: 'utf8' });
|
||||
} catch (error) {
|
||||
throw new Error(`Command failed: ${command}\n${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
function getLatestVersionFromNpm(packageName) {
|
||||
try {
|
||||
const result = execCommand(`npm view ${packageName} version`);
|
||||
return result.trim();
|
||||
} catch (error) {
|
||||
const errorMessage = error.message || error.toString();
|
||||
|
||||
if (errorMessage.includes('404')) {
|
||||
log(`Package ${packageName} not found on npm registry (404) - treating as new package`);
|
||||
return null;
|
||||
}
|
||||
|
||||
log(`ERROR: Failed to check npm registry for ${packageName}: ${errorMessage}`);
|
||||
throw new Error(`Failed to check npm registry for ${packageName}: ${errorMessage}`);
|
||||
}
|
||||
}
|
||||
|
||||
function getCurrentVersion(packagePath) {
|
||||
const packageJson = JSON.parse(fs.readFileSync(path.join(packagePath, 'package.json'), 'utf8'));
|
||||
return packageJson.version;
|
||||
}
|
||||
|
||||
function bumpVersion(currentVersion, latestNpmVersion) {
|
||||
const [major, minor, patch] = currentVersion.split('.').map(Number);
|
||||
|
||||
if (latestNpmVersion === null) {
|
||||
return currentVersion;
|
||||
}
|
||||
|
||||
const [npmMajor, npmMinor, npmPatch] = latestNpmVersion.split('.').map(Number);
|
||||
|
||||
if (currentVersion > latestNpmVersion) {
|
||||
return `${major}.${minor}.${patch + 1}`;
|
||||
}
|
||||
|
||||
if (latestNpmVersion > currentVersion) {
|
||||
return `${npmMajor}.${npmMinor}.${npmPatch + 1}`;
|
||||
}
|
||||
|
||||
return `${major}.${minor}.${patch + 1}`;
|
||||
}
|
||||
|
||||
function updatePackageVersion(packagePath, packageName, newVersion) {
|
||||
log(`Updating ${packageName} to version ${newVersion}`);
|
||||
|
||||
const packageJsonPath = path.join(packagePath, 'package.json');
|
||||
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
||||
|
||||
packageJson.version = newVersion;
|
||||
fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2) + '\n');
|
||||
|
||||
return newVersion;
|
||||
}
|
||||
|
||||
function publishPackage(packagePath, packageName) {
|
||||
log(`Publishing ${packageName}`);
|
||||
|
||||
try {
|
||||
execCommand('npm publish --provenance --access public', packagePath);
|
||||
log(`Successfully published ${packageName}`);
|
||||
} catch (error) {
|
||||
log(`Failed to publish ${packageName}: ${error.message}`);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
function main() {
|
||||
const isDryRun = process.argv.includes('--dry-run');
|
||||
|
||||
if (isDryRun) {
|
||||
log('DRY RUN MODE - No packages will be published');
|
||||
} else {
|
||||
log('Starting force publish - will publish ALL packages regardless of changes');
|
||||
}
|
||||
|
||||
log('Discovering packages...');
|
||||
const packagePaths = discoverPackages();
|
||||
log(`Found ${Object.keys(packagePaths).length} publishable packages: ${Object.keys(packagePaths).join(', ')}`);
|
||||
|
||||
log('Installing dependencies...');
|
||||
execCommand('npm ci');
|
||||
|
||||
log('Building all packages...');
|
||||
execCommand('npm run build');
|
||||
|
||||
const allPackages = Object.keys(packagePaths);
|
||||
log(`Force publishing ${allPackages.length} packages: ${allPackages.join(', ')}`);
|
||||
|
||||
for (const packageName of allPackages) {
|
||||
const packagePath = packagePaths[packageName];
|
||||
const currentVersion = getCurrentVersion(packagePath);
|
||||
const latestNpmVersion = getLatestVersionFromNpm(packageName);
|
||||
|
||||
if (latestNpmVersion === null) {
|
||||
log(`Current version: ${currentVersion}, Latest on npm: (new package)`);
|
||||
} else {
|
||||
log(`Current version: ${currentVersion}, Latest on npm: ${latestNpmVersion}`);
|
||||
}
|
||||
|
||||
const newVersion = bumpVersion(currentVersion, latestNpmVersion);
|
||||
updatePackageVersion(packagePath, packageName, newVersion);
|
||||
|
||||
if (!isDryRun) {
|
||||
publishPackage(packagePath, packageName);
|
||||
} else {
|
||||
log(`[DRY RUN] Would publish ${packageName}@${newVersion}`);
|
||||
}
|
||||
}
|
||||
|
||||
if (isDryRun) {
|
||||
log('Dry run completed - no packages were actually published');
|
||||
} else {
|
||||
log('Force publishing completed successfully');
|
||||
}
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
main();
|
||||
}
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
"packages/*"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "npm run build --workspaces",
|
||||
"build": "npm run build -w packages/core -w packages/dapp -w packages/wallet -w packages/test-cli",
|
||||
"test": "npm run test --workspaces --if-present",
|
||||
"test:integration": "npm run test:integration --workspaces --if-present",
|
||||
"dapp": "npm run dapp --workspace @wizardconnect/test-cli",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@wizardconnect/core",
|
||||
"version": "0.1.0",
|
||||
"version": "0.1.2",
|
||||
"type": "module",
|
||||
"description": "Transport and protocol primitives for WizardConnect",
|
||||
"main": "dist/index.js",
|
||||
|
|
@ -11,11 +11,11 @@
|
|||
"scripts": {
|
||||
"build": "tsc",
|
||||
"test": "vitest --config vitest.config.ts --run --passWithNoTests",
|
||||
"lint:prettier": "prettier . --list-different",
|
||||
"lint:prettier": "prettier --ignore-path ../../.gitignore . --list-different",
|
||||
"lint:eslint": "eslint .",
|
||||
"lint": "npm run lint:eslint && npm run lint:prettier",
|
||||
"fix": "npm run fix:eslint && npm run fix:prettier",
|
||||
"fix:prettier": "prettier . --write",
|
||||
"fix:prettier": "prettier --ignore-path ../../.gitignore . --write",
|
||||
"fix:eslint": "npm run lint:eslint -- --fix"
|
||||
},
|
||||
"dependencies": {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@wizardconnect/dapp",
|
||||
"version": "0.1.0",
|
||||
"version": "0.1.2",
|
||||
"type": "module",
|
||||
"description": "Dapp-side integration helpers for WizardConnect",
|
||||
"main": "dist/index.js",
|
||||
|
|
@ -11,11 +11,11 @@
|
|||
"scripts": {
|
||||
"build": "tsc",
|
||||
"test": "vitest --config vitest.config.ts --run --passWithNoTests",
|
||||
"lint:prettier": "prettier . --list-different",
|
||||
"lint:prettier": "prettier --ignore-path ../../.gitignore . --list-different",
|
||||
"lint:eslint": "eslint .",
|
||||
"lint": "npm run lint:eslint && npm run lint:prettier",
|
||||
"fix": "npm run fix:eslint && npm run fix:prettier",
|
||||
"fix:prettier": "prettier . --write",
|
||||
"fix:prettier": "prettier --ignore-path ../../.gitignore . --write",
|
||||
"fix:eslint": "npm run lint:eslint -- --fix"
|
||||
},
|
||||
"dependencies": {
|
||||
|
|
|
|||
|
|
@ -12,11 +12,11 @@
|
|||
"dev": "tsx src/cli.ts",
|
||||
"dapp": "tsx src/cli.ts dapp",
|
||||
"wallet": "tsx src/cli.ts wallet",
|
||||
"lint:prettier": "prettier . --list-different",
|
||||
"lint:prettier": "prettier --ignore-path ../../.gitignore . --list-different",
|
||||
"lint:eslint": "eslint .",
|
||||
"lint": "npm run lint:eslint && npm run lint:prettier",
|
||||
"fix": "npm run fix:eslint && npm run fix:prettier",
|
||||
"fix:prettier": "prettier . --write",
|
||||
"fix:prettier": "prettier --ignore-path ../../.gitignore . --write",
|
||||
"fix:eslint": "npm run lint:eslint -- --fix"
|
||||
},
|
||||
"dependencies": {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@wizardconnect/wallet",
|
||||
"version": "0.1.0",
|
||||
"version": "0.1.2",
|
||||
"type": "module",
|
||||
"description": "Wallet-side integration helpers for WizardConnect",
|
||||
"main": "dist/index.js",
|
||||
|
|
@ -12,11 +12,11 @@
|
|||
"build": "tsc",
|
||||
"test": "vitest --config vitest.config.ts --run --passWithNoTests",
|
||||
"test:integration": "vitest --config vitest.integration.config.ts --run",
|
||||
"lint:prettier": "prettier . --list-different",
|
||||
"lint:prettier": "prettier --ignore-path ../../.gitignore . --list-different",
|
||||
"lint:eslint": "eslint .",
|
||||
"lint": "npm run lint:eslint && npm run lint:prettier",
|
||||
"fix": "npm run fix:eslint && npm run fix:prettier",
|
||||
"fix:prettier": "prettier . --write",
|
||||
"fix:prettier": "prettier --ignore-path ../../.gitignore . --write",
|
||||
"fix:eslint": "npm run lint:eslint -- --fix"
|
||||
},
|
||||
"dependencies": {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue