chore: implement pre-release for extensions (#1097)
* chore: implement pre-release for extensions * chore: run format
This commit is contained in:
54
.github/scripts/pre-release.mjs
vendored
Executable file
54
.github/scripts/pre-release.mjs
vendored
Executable file
@@ -0,0 +1,54 @@
|
||||
#!/usr/bin/env node
|
||||
import { readFileSync, existsSync } from 'node:fs';
|
||||
import { join, dirname } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import {
|
||||
findRootDir,
|
||||
runCommand,
|
||||
getPackageVersion,
|
||||
createAndPushTag
|
||||
} from './utils.mjs';
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = dirname(__filename);
|
||||
|
||||
const rootDir = findRootDir(__dirname);
|
||||
const extensionPkgPath = join(rootDir, 'apps', 'extension', 'package.json');
|
||||
|
||||
console.log('🚀 Starting pre-release process...');
|
||||
|
||||
// Check if we're in RC mode
|
||||
const preJsonPath = join(rootDir, '.changeset', 'pre.json');
|
||||
if (!existsSync(preJsonPath)) {
|
||||
console.error('⚠️ Not in RC mode. Run "npx changeset pre enter rc" first.');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
try {
|
||||
const preJson = JSON.parse(readFileSync(preJsonPath, 'utf8'));
|
||||
if (preJson.tag !== 'rc') {
|
||||
console.error(`⚠️ Not in RC mode. Current tag: ${preJson.tag}`);
|
||||
process.exit(1);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to read pre.json:', error.message);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Get current extension version
|
||||
const extensionVersion = getPackageVersion(extensionPkgPath);
|
||||
console.log(`Extension version: ${extensionVersion}`);
|
||||
|
||||
// Run changeset publish for npm packages
|
||||
console.log('📦 Publishing npm packages...');
|
||||
runCommand('npx', ['changeset', 'publish']);
|
||||
|
||||
// Create tag for extension pre-release if it doesn't exist
|
||||
const extensionTag = `extension-rc@${extensionVersion}`;
|
||||
const tagCreated = createAndPushTag(extensionTag);
|
||||
|
||||
if (tagCreated) {
|
||||
console.log('This will trigger the extension-pre-release workflow...');
|
||||
}
|
||||
|
||||
console.log('✅ Pre-release process completed!');
|
||||
30
.github/scripts/release.mjs
vendored
Executable file
30
.github/scripts/release.mjs
vendored
Executable file
@@ -0,0 +1,30 @@
|
||||
#!/usr/bin/env node
|
||||
import { existsSync, unlinkSync } from 'node:fs';
|
||||
import { join, dirname } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { findRootDir, runCommand } from './utils.mjs';
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = dirname(__filename);
|
||||
|
||||
const rootDir = findRootDir(__dirname);
|
||||
|
||||
console.log('🚀 Starting release process...');
|
||||
|
||||
// Double-check we're not in pre-release mode (safety net)
|
||||
const preJsonPath = join(rootDir, '.changeset', 'pre.json');
|
||||
if (existsSync(preJsonPath)) {
|
||||
console.log('⚠️ Warning: pre.json still exists. Removing it...');
|
||||
unlinkSync(preJsonPath);
|
||||
}
|
||||
|
||||
// Check if the extension version has changed and tag it
|
||||
// This prevents changeset from trying to publish the private package
|
||||
runCommand('node', [join(__dirname, 'tag-extension.mjs')]);
|
||||
|
||||
// Run changeset publish for npm packages
|
||||
runCommand('npx', ['changeset', 'publish']);
|
||||
|
||||
console.log('✅ Release process completed!');
|
||||
|
||||
// The extension tag (if created) will trigger the extension-release workflow
|
||||
21
.github/scripts/release.sh
vendored
21
.github/scripts/release.sh
vendored
@@ -1,21 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
echo "🚀 Starting release process..."
|
||||
|
||||
# Double-check we're not in pre-release mode (safety net)
|
||||
if [ -f .changeset/pre.json ]; then
|
||||
echo "⚠️ Warning: pre.json still exists. Removing it..."
|
||||
rm -f .changeset/pre.json
|
||||
fi
|
||||
|
||||
# Check if the extension version has changed and tag it
|
||||
# This prevents changeset from trying to publish the private package
|
||||
node .github/scripts/tag-extension.mjs
|
||||
|
||||
# Run changeset publish for npm packages
|
||||
npx changeset publish
|
||||
|
||||
echo "✅ Release process completed!"
|
||||
|
||||
# The extension tag (if created) will trigger the extension-release workflow
|
||||
114
.github/scripts/tag-extension.mjs
vendored
Normal file → Executable file
114
.github/scripts/tag-extension.mjs
vendored
Normal file → Executable file
@@ -1,33 +1,13 @@
|
||||
#!/usr/bin/env node
|
||||
import assert from 'node:assert/strict';
|
||||
import { spawnSync } from 'node:child_process';
|
||||
import { readFileSync, existsSync } from 'node:fs';
|
||||
import { join, dirname, resolve } from 'node:path';
|
||||
import { readFileSync } from 'node:fs';
|
||||
import { join, dirname } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { findRootDir, createAndPushTag } from './utils.mjs';
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = dirname(__filename);
|
||||
|
||||
// Find the root directory by looking for package.json
|
||||
function findRootDir(startDir) {
|
||||
let currentDir = resolve(startDir);
|
||||
while (currentDir !== '/') {
|
||||
if (existsSync(join(currentDir, 'package.json'))) {
|
||||
// Verify it's the root package.json by checking for expected fields
|
||||
try {
|
||||
const pkg = JSON.parse(
|
||||
readFileSync(join(currentDir, 'package.json'), 'utf8')
|
||||
);
|
||||
if (pkg.name === 'task-master-ai' || pkg.repository) {
|
||||
return currentDir;
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
currentDir = dirname(currentDir);
|
||||
}
|
||||
throw new Error('Could not find root directory');
|
||||
}
|
||||
|
||||
const rootDir = findRootDir(__dirname);
|
||||
|
||||
// Read the extension's package.json
|
||||
@@ -43,95 +23,11 @@ try {
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Read root package.json for repository info
|
||||
const rootPkgPath = join(rootDir, 'package.json');
|
||||
let rootPkg;
|
||||
try {
|
||||
const rootPkgContent = readFileSync(rootPkgPath, 'utf8');
|
||||
rootPkg = JSON.parse(rootPkgContent);
|
||||
} catch (error) {
|
||||
console.error('Failed to read root package.json:', error.message);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Ensure we have required fields
|
||||
assert(pkg.name, 'package.json must have a name field');
|
||||
assert(pkg.version, 'package.json must have a version field');
|
||||
assert(rootPkg.repository, 'root package.json must have a repository field');
|
||||
|
||||
const tag = `${pkg.name}@${pkg.version}`;
|
||||
|
||||
// Get repository URL from root package.json
|
||||
// Get repository URL and clean it up for git ls-remote
|
||||
let repoUrl = rootPkg.repository.url || rootPkg.repository;
|
||||
if (typeof repoUrl === 'string') {
|
||||
// Convert git+https://github.com/... to https://github.com/...
|
||||
repoUrl = repoUrl.replace(/^git\+/, '');
|
||||
// Ensure it ends with .git for proper remote access
|
||||
if (!repoUrl.endsWith('.git')) {
|
||||
repoUrl += '.git';
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`Checking remote repository: ${repoUrl} for tag: ${tag}`);
|
||||
|
||||
let gitResult = spawnSync('git', ['ls-remote', repoUrl, tag], {
|
||||
encoding: 'utf8',
|
||||
env: { ...process.env }
|
||||
});
|
||||
|
||||
if (gitResult.status !== 0) {
|
||||
console.error('Git ls-remote failed:');
|
||||
console.error('Exit code:', gitResult.status);
|
||||
console.error('Error:', gitResult.error);
|
||||
console.error('Stderr:', gitResult.stderr);
|
||||
console.error('Command:', `git ls-remote ${repoUrl} ${tag}`);
|
||||
|
||||
// For CI environments, try using origin instead of the full URL
|
||||
if (process.env.CI) {
|
||||
console.log('Retrying with origin remote...');
|
||||
gitResult = spawnSync('git', ['ls-remote', 'origin', tag], {
|
||||
encoding: 'utf8'
|
||||
});
|
||||
|
||||
if (gitResult.status !== 0) {
|
||||
throw new Error(
|
||||
`Failed to check remote for tag ${tag}. Exit code: ${gitResult.status}`
|
||||
);
|
||||
}
|
||||
} else {
|
||||
throw new Error(
|
||||
`Failed to check remote for tag ${tag}. Exit code: ${gitResult.status}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const exists = String(gitResult.stdout).trim() !== '';
|
||||
|
||||
if (!exists) {
|
||||
console.log(`Creating new extension tag: ${tag}`);
|
||||
|
||||
// Create the tag
|
||||
const tagResult = spawnSync('git', ['tag', tag]);
|
||||
if (tagResult.status !== 0) {
|
||||
console.error(
|
||||
'Failed to create tag:',
|
||||
tagResult.error || tagResult.stderr.toString()
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Push the tag
|
||||
const pushResult = spawnSync('git', ['push', 'origin', tag]);
|
||||
if (pushResult.status !== 0) {
|
||||
console.error(
|
||||
'Failed to push tag:',
|
||||
pushResult.error || pushResult.stderr.toString()
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log(`✅ Successfully created and pushed tag: ${tag}`);
|
||||
} else {
|
||||
console.log(`Extension tag already exists: ${tag}`);
|
||||
}
|
||||
// Create and push the tag if it doesn't exist
|
||||
createAndPushTag(tag);
|
||||
|
||||
88
.github/scripts/utils.mjs
vendored
Executable file
88
.github/scripts/utils.mjs
vendored
Executable file
@@ -0,0 +1,88 @@
|
||||
#!/usr/bin/env node
|
||||
import { spawnSync } from 'node:child_process';
|
||||
import { readFileSync } from 'node:fs';
|
||||
import { join, dirname, resolve } from 'node:path';
|
||||
|
||||
// Find the root directory by looking for package.json with task-master-ai
|
||||
export function findRootDir(startDir) {
|
||||
let currentDir = resolve(startDir);
|
||||
while (currentDir !== '/') {
|
||||
const pkgPath = join(currentDir, 'package.json');
|
||||
try {
|
||||
const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));
|
||||
if (pkg.name === 'task-master-ai' || pkg.repository) {
|
||||
return currentDir;
|
||||
}
|
||||
} catch {}
|
||||
currentDir = dirname(currentDir);
|
||||
}
|
||||
throw new Error('Could not find root directory');
|
||||
}
|
||||
|
||||
// Run a command with proper error handling
|
||||
export function runCommand(command, args = [], options = {}) {
|
||||
console.log(`Running: ${command} ${args.join(' ')}`);
|
||||
const result = spawnSync(command, args, {
|
||||
encoding: 'utf8',
|
||||
stdio: 'inherit',
|
||||
...options
|
||||
});
|
||||
|
||||
if (result.status !== 0) {
|
||||
console.error(`Command failed with exit code ${result.status}`);
|
||||
process.exit(result.status);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Get package version from a package.json file
|
||||
export function getPackageVersion(packagePath) {
|
||||
try {
|
||||
const pkg = JSON.parse(readFileSync(packagePath, 'utf8'));
|
||||
return pkg.version;
|
||||
} catch (error) {
|
||||
console.error(
|
||||
`Failed to read package version from ${packagePath}:`,
|
||||
error.message
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Check if a git tag exists on remote
|
||||
export function tagExistsOnRemote(tag, remote = 'origin') {
|
||||
const result = spawnSync('git', ['ls-remote', remote, tag], {
|
||||
encoding: 'utf8'
|
||||
});
|
||||
|
||||
return result.status === 0 && result.stdout.trim() !== '';
|
||||
}
|
||||
|
||||
// Create and push a git tag if it doesn't exist
|
||||
export function createAndPushTag(tag, remote = 'origin') {
|
||||
// Check if tag already exists
|
||||
if (tagExistsOnRemote(tag, remote)) {
|
||||
console.log(`Tag ${tag} already exists on remote, skipping`);
|
||||
return false;
|
||||
}
|
||||
|
||||
console.log(`Creating new tag: ${tag}`);
|
||||
|
||||
// Create the tag locally
|
||||
const tagResult = spawnSync('git', ['tag', tag]);
|
||||
if (tagResult.status !== 0) {
|
||||
console.error('Failed to create tag:', tagResult.error || tagResult.stderr);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Push the tag to remote
|
||||
const pushResult = spawnSync('git', ['push', remote, tag]);
|
||||
if (pushResult.status !== 0) {
|
||||
console.error('Failed to push tag:', pushResult.error || pushResult.stderr);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log(`✅ Successfully created and pushed tag: ${tag}`);
|
||||
return true;
|
||||
}
|
||||
Reference in New Issue
Block a user