diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index aa72f59..b0e6c13 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -55,7 +55,7 @@ jobs: - name: Setup Node.js uses: actions/setup-node@v4 with: - node-version: 22 + node-version: 24 registry-url: 'https://registry.npmjs.org' cache: 'npm' diff --git a/package-lock.json b/package-lock.json index 8be57d5..f256356 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@jackmisner/utm-toolkit", - "version": "0.1.0", + "version": "0.1.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@jackmisner/utm-toolkit", - "version": "0.1.0", + "version": "0.1.1", "license": "MIT", "devDependencies": { "@testing-library/react": "^16.0.0", diff --git a/package.json b/package.json index cc2af0b..ab8ed0b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@jackmisner/utm-toolkit", - "version": "0.1.0", + "version": "0.1.1", "description": "Capture, store, and append UTM tracking parameters", "type": "module", "main": "./dist/cjs/index.js", @@ -47,7 +47,10 @@ "lint:fix": "oxlint --fix", "format": "prettier --write \"src/**/*.{ts,tsx}\" \"__tests__/**/*.{ts,tsx}\"", "format:check": "prettier --check \"src/**/*.{ts,tsx}\" \"__tests__/**/*.{ts,tsx}\"", - "prepublishOnly": "npm run clean && npm run build && npm run test" + "prepublishOnly": "npm run clean && npm run build && npm run test", + "release:patch": "node scripts/release.js patch", + "release:minor": "node scripts/release.js minor", + "release:major": "node scripts/release.js major" }, "peerDependencies": { "react": ">=16.8.0" diff --git a/scripts/release.js b/scripts/release.js new file mode 100644 index 0000000..09eccdc --- /dev/null +++ b/scripts/release.js @@ -0,0 +1,118 @@ +#!/usr/bin/env node + +/** + * Release script that creates a release branch, bumps version, and pushes. + * + * Usage: node scripts/release.js + */ + +import { execSync } from 'child_process' +import { readFileSync } from 'fs' +import { fileURLToPath } from 'url' +import { dirname, join } from 'path' + +const __dirname = dirname(fileURLToPath(import.meta.url)) + +function run(cmd, options = {}) { + console.log(`$ ${cmd}`) + return execSync(cmd, { stdio: 'inherit', ...options }) +} + +function runCapture(cmd) { + return execSync(cmd, { encoding: 'utf-8' }).trim() +} + +function calculateNewVersion(current, type) { + const [major, minor, patch] = current.split('.').map(Number) + + switch (type) { + case 'major': + return `${major + 1}.0.0` + case 'minor': + return `${major}.${minor + 1}.0` + case 'patch': + return `${major}.${minor}.${patch + 1}` + default: + throw new Error(`Invalid version type: ${type}`) + } +} + +function getRemoteName() { + const remotes = runCapture('git remote').split('\n').filter(Boolean) + if (remotes.length === 0) { + console.error('āŒ No git remotes configured.') + process.exit(1) + } + // Prefer 'origin', otherwise use the first remote + return remotes.includes('origin') ? 'origin' : remotes[0] +} + +function main() { + const type = process.argv[2] + + if (!['patch', 'minor', 'major'].includes(type)) { + console.error('Usage: node scripts/release.js ') + process.exit(1) + } + + const remote = getRemoteName() + console.log(`šŸ“” Using remote: ${remote}`) + + // Read current version + const packagePath = join(__dirname, '..', 'package.json') + const packageJson = JSON.parse(readFileSync(packagePath, 'utf-8')) + const currentVersion = packageJson.version + + // Calculate new version + const newVersion = calculateNewVersion(currentVersion, type) + const branchName = `release/${newVersion}` + + console.log(`\nšŸ“¦ Release: ${currentVersion} → ${newVersion}\n`) + + // Check we're on main + const currentBranch = runCapture('git branch --show-current') + if (currentBranch !== 'main') { + console.error(`āŒ Must be on main branch to create a release. Currently on: ${currentBranch}`) + process.exit(1) + } + + // Pull latest + console.log('šŸ“„ Pulling latest from main...') + run(`git pull ${remote} main`) + + // Check for uncommitted changes + const status = runCapture('git status --porcelain') + if (status) { + console.error(`āŒ Working directory has uncommitted changes. + +Please review and commit your changes before releasing v${newVersion}: +Then run the release command again. +`) + process.exit(1) + } + + // Create release branch + console.log(`\n🌿 Creating branch: ${branchName}`) + run(`git checkout -b ${branchName}`) + + // Bump version + console.log(`\nšŸ“ Bumping version to ${newVersion}...`) + run(`npm version ${type}`) + + // Push branch and tags + console.log(`\nšŸš€ Pushing ${branchName} with tags...`) + run(`git push -u ${remote} ${branchName} --follow-tags`) + + console.log(` +āœ… Release branch created and pushed! + +Next steps: +1. Create a PR: https://github.com/jackmisner/utm-toolkit/compare/main...${branchName} +2. Merge the PR to main +3. The tag (v${newVersion}) will trigger the publish workflow + +Note: The npm publish will happen automatically when the tag is pushed. +`) +} + +main()