@muverse/core 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +22 -0
- package/dist/adapters/gradle/constants.d.ts +13 -0
- package/dist/adapters/gradle/constants.d.ts.map +1 -0
- package/dist/adapters/gradle/constants.js +12 -0
- package/dist/adapters/gradle/gradle-project-information.d.ts +18 -0
- package/dist/adapters/gradle/gradle-project-information.d.ts.map +1 -0
- package/dist/adapters/gradle/gradle-project-information.js +93 -0
- package/dist/adapters/gradle/gradle-properties.d.ts +15 -0
- package/dist/adapters/gradle/gradle-properties.d.ts.map +1 -0
- package/dist/adapters/gradle/gradle-properties.js +46 -0
- package/dist/adapters/gradle/init-project-information.gradle.kts +143 -0
- package/dist/adapters/gradle/services/gradle-adapter-identifier.d.ts +21 -0
- package/dist/adapters/gradle/services/gradle-adapter-identifier.d.ts.map +1 -0
- package/dist/adapters/gradle/services/gradle-adapter-identifier.js +44 -0
- package/dist/adapters/gradle/services/gradle-module-detector.d.ts +18 -0
- package/dist/adapters/gradle/services/gradle-module-detector.d.ts.map +1 -0
- package/dist/adapters/gradle/services/gradle-module-detector.js +26 -0
- package/dist/adapters/gradle/services/gradle-module-system-factory.d.ts +23 -0
- package/dist/adapters/gradle/services/gradle-module-system-factory.d.ts.map +1 -0
- package/dist/adapters/gradle/services/gradle-module-system-factory.js +27 -0
- package/dist/adapters/gradle/services/gradle-version-update-strategy.d.ts +21 -0
- package/dist/adapters/gradle/services/gradle-version-update-strategy.d.ts.map +1 -0
- package/dist/adapters/gradle/services/gradle-version-update-strategy.js +36 -0
- package/dist/adapters/project-information.d.ts +58 -0
- package/dist/adapters/project-information.d.ts.map +1 -0
- package/dist/adapters/project-information.js +1 -0
- package/dist/changelog/index.d.ts +27 -0
- package/dist/changelog/index.d.ts.map +1 -0
- package/dist/changelog/index.js +204 -0
- package/dist/config/index.d.ts +122 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +115 -0
- package/dist/factories/adapter-identifier-registry.d.ts +12 -0
- package/dist/factories/adapter-identifier-registry.d.ts.map +1 -0
- package/dist/factories/adapter-identifier-registry.js +24 -0
- package/dist/factories/module-system-factory.d.ts +10 -0
- package/dist/factories/module-system-factory.d.ts.map +1 -0
- package/dist/factories/module-system-factory.js +18 -0
- package/dist/git/index.d.ts +253 -0
- package/dist/git/index.d.ts.map +1 -0
- package/dist/git/index.js +581 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +22 -0
- package/dist/semver/index.d.ts +85 -0
- package/dist/semver/index.d.ts.map +1 -0
- package/dist/semver/index.js +176 -0
- package/dist/services/adapter-identifier-registry.d.ts +38 -0
- package/dist/services/adapter-identifier-registry.d.ts.map +1 -0
- package/dist/services/adapter-identifier-registry.js +59 -0
- package/dist/services/adapter-identifier.d.ts +31 -0
- package/dist/services/adapter-identifier.d.ts.map +1 -0
- package/dist/services/adapter-identifier.js +1 -0
- package/dist/services/adapter-metadata-provider.d.ts +51 -0
- package/dist/services/adapter-metadata-provider.d.ts.map +1 -0
- package/dist/services/adapter-metadata-provider.js +66 -0
- package/dist/services/changelog-generator.d.ts +13 -0
- package/dist/services/changelog-generator.d.ts.map +1 -0
- package/dist/services/changelog-generator.js +26 -0
- package/dist/services/commit-analyzer.d.ts +44 -0
- package/dist/services/commit-analyzer.d.ts.map +1 -0
- package/dist/services/commit-analyzer.js +86 -0
- package/dist/services/configuration-loader.d.ts +23 -0
- package/dist/services/configuration-loader.d.ts.map +1 -0
- package/dist/services/configuration-loader.js +79 -0
- package/dist/services/configuration-validator.d.ts +16 -0
- package/dist/services/configuration-validator.d.ts.map +1 -0
- package/dist/services/configuration-validator.js +24 -0
- package/dist/services/git-operations.d.ts +16 -0
- package/dist/services/git-operations.d.ts.map +1 -0
- package/dist/services/git-operations.js +89 -0
- package/dist/services/module-detector.d.ts +24 -0
- package/dist/services/module-detector.d.ts.map +1 -0
- package/dist/services/module-detector.js +1 -0
- package/dist/services/module-registry.d.ts +45 -0
- package/dist/services/module-registry.d.ts.map +1 -0
- package/dist/services/module-registry.js +57 -0
- package/dist/services/module-system-factory.d.ts +24 -0
- package/dist/services/module-system-factory.d.ts.map +1 -0
- package/dist/services/module-system-factory.js +1 -0
- package/dist/services/verse-runner.d.ts +45 -0
- package/dist/services/verse-runner.d.ts.map +1 -0
- package/dist/services/verse-runner.js +182 -0
- package/dist/services/version-applier.d.ts +26 -0
- package/dist/services/version-applier.d.ts.map +1 -0
- package/dist/services/version-applier.js +63 -0
- package/dist/services/version-bumper.d.ts +156 -0
- package/dist/services/version-bumper.d.ts.map +1 -0
- package/dist/services/version-bumper.js +291 -0
- package/dist/services/version-manager.d.ts +68 -0
- package/dist/services/version-manager.d.ts.map +1 -0
- package/dist/services/version-manager.js +94 -0
- package/dist/services/version-update-strategy.d.ts +18 -0
- package/dist/services/version-update-strategy.d.ts.map +1 -0
- package/dist/services/version-update-strategy.js +1 -0
- package/dist/utils/banner.d.ts +2 -0
- package/dist/utils/banner.d.ts.map +1 -0
- package/dist/utils/banner.js +8 -0
- package/dist/utils/commits.d.ts +12 -0
- package/dist/utils/commits.d.ts.map +1 -0
- package/dist/utils/commits.js +24 -0
- package/dist/utils/file.d.ts +7 -0
- package/dist/utils/file.d.ts.map +1 -0
- package/dist/utils/file.js +19 -0
- package/dist/utils/index.d.ts +6 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +5 -0
- package/dist/utils/logger.d.ts +14 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +22 -0
- package/dist/utils/properties.d.ts +16 -0
- package/dist/utils/properties.d.ts.map +1 -0
- package/dist/utils/properties.js +62 -0
- package/dist/utils/versioning.d.ts +8 -0
- package/dist/utils/versioning.d.ts.map +1 -0
- package/dist/utils/versioning.js +17 -0
- package/package.json +70 -0
|
@@ -0,0 +1,581 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Git operations module for μVERSE version management.
|
|
3
|
+
* Provides interfaces for commit analysis, tagging, and conventional commit parsing.
|
|
4
|
+
* Supports monorepo and multi-module projects with module-specific tag management.
|
|
5
|
+
*/
|
|
6
|
+
import { CommitParser } from "conventional-commits-parser";
|
|
7
|
+
import { logger } from "../utils/logger.js";
|
|
8
|
+
import { execa } from "execa";
|
|
9
|
+
/**
|
|
10
|
+
* Shared CommitParser instance for parsing conventional commits.
|
|
11
|
+
* Reused across all commit parsing operations to avoid repeated instantiation.
|
|
12
|
+
*/
|
|
13
|
+
const commitParser = new CommitParser({
|
|
14
|
+
breakingHeaderPattern: /^(\w*)(?:\(([\w$@.\-*/ ]*)\))?!: (.*)$/,
|
|
15
|
+
});
|
|
16
|
+
/**
|
|
17
|
+
* Retrieves all commits for a module since its last release tag.
|
|
18
|
+
* Handles monorepo and single-repo scenarios with path filtering.
|
|
19
|
+
* @param modulePath - Relative path to module from repository root (use '.' for root)
|
|
20
|
+
* @param moduleName - Module name used for tag searching
|
|
21
|
+
* @param moduleType - 'root' for general tags, 'module' for module-specific tags
|
|
22
|
+
* @param options - Git operation options
|
|
23
|
+
* @param excludePaths - Paths to exclude using git pathspec syntax
|
|
24
|
+
* @returns Promise resolving to array of parsed commits (oldest to newest)
|
|
25
|
+
*/
|
|
26
|
+
export async function getCommitsSinceLastTag(modulePath, moduleName, moduleType, options = {}, excludePaths = []) {
|
|
27
|
+
// Resolve the working directory, defaulting to current process directory
|
|
28
|
+
const cwd = options.cwd || process.cwd();
|
|
29
|
+
try {
|
|
30
|
+
// Find the most recent tag for this module
|
|
31
|
+
// For root modules, this finds general tags (v1.0.0)
|
|
32
|
+
// For submodules, this finds module-specific tags (module@1.0.0)
|
|
33
|
+
const lastTag = await getLastTagForModule(moduleName, moduleType, { cwd });
|
|
34
|
+
// Build the git revision range
|
|
35
|
+
// If tag exists: 'tag..HEAD' means commits after tag up to HEAD
|
|
36
|
+
// If no tag: empty string means all commits in history
|
|
37
|
+
const range = lastTag ? `${lastTag}..HEAD` : "";
|
|
38
|
+
return getCommitsInRange(range, modulePath, { cwd }, excludePaths);
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
// If tag lookup fails for any reason, fall back to all commits
|
|
42
|
+
// This ensures we always have commit history for version determination
|
|
43
|
+
return getCommitsInRange("", modulePath, { cwd }, excludePaths);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Retrieves commits within a specific git revision range with path filtering.
|
|
48
|
+
* Uses git's native pathspec syntax for efficient filtering in monorepos.
|
|
49
|
+
* @param range - Git revision range (e.g., 'tag1..tag2', 'tag..HEAD', or '' for all)
|
|
50
|
+
* @param pathFilter - Optional path to filter commits (use '.' for root)
|
|
51
|
+
* @param options - Git operation options
|
|
52
|
+
* @param excludePaths - Paths to exclude using ':(exclude)path' syntax
|
|
53
|
+
* @returns Promise resolving to array of parsed commits (oldest to newest)
|
|
54
|
+
*/
|
|
55
|
+
export async function getCommitsInRange(range, pathFilter, options = {}, excludePaths = []) {
|
|
56
|
+
// Resolve working directory, defaulting to current directory
|
|
57
|
+
const cwd = options.cwd || process.cwd();
|
|
58
|
+
try {
|
|
59
|
+
// Build git log command with custom format for easy parsing
|
|
60
|
+
// Format: hash, subject, body, delimiter
|
|
61
|
+
const args = ["log", "--format=%H%n%s%n%b%n---COMMIT-END---"];
|
|
62
|
+
// Only add range if it's not empty
|
|
63
|
+
// Empty range means "all commits" which is valid
|
|
64
|
+
if (range.trim()) {
|
|
65
|
+
args.push(range);
|
|
66
|
+
}
|
|
67
|
+
// Add pathspec separator ('--') if we have paths or excludes
|
|
68
|
+
// This separates revision arguments from path arguments
|
|
69
|
+
// Add path filter if provided and not root
|
|
70
|
+
if (pathFilter && pathFilter !== ".") {
|
|
71
|
+
args.push("--", pathFilter);
|
|
72
|
+
}
|
|
73
|
+
else if (excludePaths.length > 0) {
|
|
74
|
+
// For root path, we still need to add the pathspec separator
|
|
75
|
+
// when we have exclude patterns
|
|
76
|
+
args.push("--");
|
|
77
|
+
}
|
|
78
|
+
// Add each exclude pattern using git's pathspec syntax
|
|
79
|
+
// :(exclude)path tells git to ignore commits touching that path
|
|
80
|
+
for (const excludePath of excludePaths) {
|
|
81
|
+
if (excludePath && excludePath !== ".") {
|
|
82
|
+
args.push(`:(exclude)${excludePath}`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
// Execute git log command
|
|
86
|
+
// Silent mode prevents output pollution in GitHub Actions
|
|
87
|
+
const { stdout } = await execa("git", args, { cwd });
|
|
88
|
+
// Parse the formatted output into CommitInfo objects
|
|
89
|
+
return parseGitLog(stdout);
|
|
90
|
+
}
|
|
91
|
+
catch (error) {
|
|
92
|
+
// Non-throwing error handling: log warning and return empty array
|
|
93
|
+
// This allows the system to continue even if git operations fail
|
|
94
|
+
logger.warning(`Warning: Failed to get git commits: ${error}`);
|
|
95
|
+
return [];
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Parses raw git log output into structured CommitInfo objects with Conventional Commits analysis.
|
|
100
|
+
* Resilient to parsing failures - classifies non-conventional commits as 'unknown' type.
|
|
101
|
+
* @param output - Raw git log output using custom format
|
|
102
|
+
* @returns Array of parsed CommitInfo objects (empty if no valid commits)
|
|
103
|
+
* @internal
|
|
104
|
+
*/
|
|
105
|
+
function parseGitLog(output) {
|
|
106
|
+
// Early return for empty output - no commits to parse
|
|
107
|
+
if (!output.trim()) {
|
|
108
|
+
return [];
|
|
109
|
+
}
|
|
110
|
+
logger.debug(`Raw git log output:\n${output}`);
|
|
111
|
+
const commits = [];
|
|
112
|
+
// Split output into individual commit blocks using custom delimiter
|
|
113
|
+
// Filter removes empty blocks (trailing delimiters, etc.)
|
|
114
|
+
const commitBlocks = output
|
|
115
|
+
.split("---COMMIT-END---")
|
|
116
|
+
.filter((block) => block.trim());
|
|
117
|
+
for (const block of commitBlocks) {
|
|
118
|
+
// Split block into lines: [hash, subject, body...]
|
|
119
|
+
const lines = block.trim().split("\n");
|
|
120
|
+
// Skip malformed blocks (need at least hash and subject)
|
|
121
|
+
if (lines.length < 2) {
|
|
122
|
+
logger.debug(`Skipping malformed commit block:\n${block}`);
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
// Extract structured data from the block
|
|
126
|
+
const hash = lines[0]; // Line 1: commit SHA
|
|
127
|
+
const subject = lines[1]; // Line 2: commit message subject
|
|
128
|
+
const body = lines.slice(2).join("\n").trim(); // Remaining: commit body
|
|
129
|
+
logger.debug(`Processing commit ${hash} with subject: ${subject}`);
|
|
130
|
+
logger.debug(`Commit body:\n${body}`);
|
|
131
|
+
try {
|
|
132
|
+
// Parse using Conventional Commits specification
|
|
133
|
+
// Combines subject and body for full context (breaking changes may be in body)
|
|
134
|
+
const parsed = commitParser.parse(subject + "\n\n" + body);
|
|
135
|
+
logger.debug(`Parsed commit ${hash}: ${JSON.stringify(parsed)}`);
|
|
136
|
+
// Build CommitInfo from parsed data
|
|
137
|
+
commits.push({
|
|
138
|
+
hash,
|
|
139
|
+
type: parsed.type || "unknown", // Default to 'unknown' if type missing
|
|
140
|
+
scope: parsed.scope || undefined,
|
|
141
|
+
subject: parsed.subject || subject, // Fallback to raw subject if parsing fails
|
|
142
|
+
body: body || undefined,
|
|
143
|
+
// Check if any note has title 'BREAKING CHANGE'
|
|
144
|
+
breaking: parsed.notes?.some((note) => note.title === "BREAKING CHANGE") ||
|
|
145
|
+
false,
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
catch (error) {
|
|
149
|
+
// If conventional commits parsing fails, treat as unknown type
|
|
150
|
+
// This ensures non-conventional commits don't break the system
|
|
151
|
+
commits.push({
|
|
152
|
+
hash,
|
|
153
|
+
type: "unknown",
|
|
154
|
+
subject,
|
|
155
|
+
body: body || undefined,
|
|
156
|
+
breaking: false,
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return commits;
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Finds the most recent git tag for a specific module with fallback to general tags.
|
|
164
|
+
* Searches module-specific tags first (moduleName@*), then falls back to general tags.
|
|
165
|
+
* @param moduleName - Module name for tag pattern construction
|
|
166
|
+
* @param moduleType - 'root' skips module tags, 'module' tries module tags first
|
|
167
|
+
* @param options - Git operation options
|
|
168
|
+
* @returns Most recent tag name or null if no tags exist
|
|
169
|
+
*/
|
|
170
|
+
export async function getLastTagForModule(moduleName, moduleType, options = {}) {
|
|
171
|
+
// Resolve working directory, defaulting to current directory
|
|
172
|
+
const cwd = options.cwd || process.cwd();
|
|
173
|
+
try {
|
|
174
|
+
// Generate glob pattern for module-specific tags (e.g., 'api@*')
|
|
175
|
+
const moduleTagPattern = getModuleTagPattern(moduleName);
|
|
176
|
+
// Only search for module-specific tags if it's not root
|
|
177
|
+
// Root projects use general tags (v1.0.0) rather than module tags (root@1.0.0)
|
|
178
|
+
if (moduleType !== "root") {
|
|
179
|
+
// Search for module-specific tags with version sorting
|
|
180
|
+
// --sort=-version:refname: Sort by version in descending order (newest first)
|
|
181
|
+
const { stdout } = await execa("git", ["tag", "-l", moduleTagPattern, "--sort=-version:refname"], {
|
|
182
|
+
cwd,
|
|
183
|
+
});
|
|
184
|
+
// If we found module-specific tags, return the first (most recent)
|
|
185
|
+
if (stdout.trim()) {
|
|
186
|
+
return stdout.trim().split("\n")[0];
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
// Fallback to general tags when:
|
|
190
|
+
// 1. Module type is 'root', or
|
|
191
|
+
// 2. No module-specific tags were found
|
|
192
|
+
try {
|
|
193
|
+
// git describe finds the most recent tag reachable from HEAD
|
|
194
|
+
// --tags: Consider all tags (not just annotated)
|
|
195
|
+
// --abbrev=0: Don't show commit hash suffix
|
|
196
|
+
const { stdout: fallbackOutput } = await execa("git", ["describe", "--tags", "--abbrev=0", "HEAD"], {
|
|
197
|
+
cwd,
|
|
198
|
+
});
|
|
199
|
+
return fallbackOutput.trim();
|
|
200
|
+
}
|
|
201
|
+
catch {
|
|
202
|
+
// If no tags at all, return null
|
|
203
|
+
// This typically means it's a new repository or no releases yet
|
|
204
|
+
return null;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
catch (error) {
|
|
208
|
+
// Catch-all error handler: return null if any unexpected error occurs
|
|
209
|
+
// This makes the function non-throwing, which is safer for version calculations
|
|
210
|
+
return null;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Retrieves all git tags in the repository with parsed metadata.
|
|
215
|
+
* Returns array with tag name, commit hash, and parsed module/version information.
|
|
216
|
+
* @param options - Git operation options
|
|
217
|
+
* @returns Promise resolving to array of GitTag objects (empty array if no tags exist)
|
|
218
|
+
*/
|
|
219
|
+
export async function getAllTags(options = {}) {
|
|
220
|
+
// Resolve working directory
|
|
221
|
+
const cwd = options.cwd || process.cwd();
|
|
222
|
+
try {
|
|
223
|
+
// List all tags with custom format to get name and commit hash
|
|
224
|
+
// %(refname:short): Tag name without refs/tags/ prefix
|
|
225
|
+
// %(objectname): Full commit SHA that the tag points to
|
|
226
|
+
const { stdout } = await execa("git", ["tag", "-l", "--format=%(refname:short) %(objectname)"], {
|
|
227
|
+
cwd,
|
|
228
|
+
});
|
|
229
|
+
// Parse each line into a GitTag object
|
|
230
|
+
return stdout
|
|
231
|
+
.trim()
|
|
232
|
+
.split("\n")
|
|
233
|
+
.filter((line) => line.trim()) // Remove empty lines
|
|
234
|
+
.map((line) => {
|
|
235
|
+
// Each line format: "tagname commithash"
|
|
236
|
+
const [name, hash] = line.split(" ");
|
|
237
|
+
// Parse tag name to extract module and version (if present)
|
|
238
|
+
const { module, version } = parseTagName(name);
|
|
239
|
+
// Return structured tag object
|
|
240
|
+
return {
|
|
241
|
+
name,
|
|
242
|
+
hash,
|
|
243
|
+
module,
|
|
244
|
+
version,
|
|
245
|
+
};
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
catch (error) {
|
|
249
|
+
// Non-throwing: return empty array if git command fails
|
|
250
|
+
// This could happen if not in a git repository or no tags exist
|
|
251
|
+
return [];
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Creates an annotated git tag at the current HEAD commit.
|
|
256
|
+
* Annotated tags include tagger metadata, date, message, and can be GPG signed.
|
|
257
|
+
* @param tagName - The tag name (e.g., 'core@1.0.0' or 'v1.0.0'). Must not already exist
|
|
258
|
+
* @param message - The annotation message for the tag
|
|
259
|
+
* @param options - Git operation options
|
|
260
|
+
* @returns Promise that resolves when the tag is successfully created
|
|
261
|
+
* @throws {Error} If tag creation fails (tag exists, invalid name, etc.)
|
|
262
|
+
*/
|
|
263
|
+
export async function createTag(tagName, message, options = {}) {
|
|
264
|
+
// Resolve working directory
|
|
265
|
+
const cwd = options.cwd || process.cwd();
|
|
266
|
+
try {
|
|
267
|
+
// Create annotated tag with message
|
|
268
|
+
// -a: Create an annotated tag (full git object)
|
|
269
|
+
// -m: Provide tag message inline
|
|
270
|
+
await execa("git", ["tag", "-a", tagName, "-m", message], { cwd });
|
|
271
|
+
}
|
|
272
|
+
catch (error) {
|
|
273
|
+
// Wrap error with more context for debugging
|
|
274
|
+
// Common failures: tag exists, no git repo, no user config
|
|
275
|
+
throw new Error(`Failed to create tag ${tagName}: ${error}`);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Pushes all local tags to the configured remote repository.
|
|
280
|
+
* Only pushes tags that don't exist on remote. Does NOT push commits.
|
|
281
|
+
* @param options - Git operation options
|
|
282
|
+
* @returns Promise that resolves when all tags are successfully pushed
|
|
283
|
+
* @throws {Error} If push fails (no remote, authentication, network, conflicts, etc.)
|
|
284
|
+
*/
|
|
285
|
+
export async function pushTags(options = {}) {
|
|
286
|
+
// Resolve working directory
|
|
287
|
+
const cwd = options.cwd || process.cwd();
|
|
288
|
+
try {
|
|
289
|
+
// Push all tags to the remote repository
|
|
290
|
+
// --tags: Push all tags (annotated and lightweight)
|
|
291
|
+
// This does NOT push commits, only tags
|
|
292
|
+
await execa("git", ["push", "--tags"], { cwd });
|
|
293
|
+
}
|
|
294
|
+
catch (error) {
|
|
295
|
+
// Wrap error with context
|
|
296
|
+
// Common failures: no remote, auth, network, conflicts
|
|
297
|
+
throw new Error(`Failed to push tags: ${error}`);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* Generates a glob pattern for searching module-specific git tags (moduleName@*).
|
|
302
|
+
* @param moduleName - The name of the module
|
|
303
|
+
* @returns A glob pattern string matching all tags for the module
|
|
304
|
+
* @internal
|
|
305
|
+
*/
|
|
306
|
+
function getModuleTagPattern(moduleName) {
|
|
307
|
+
// Create glob pattern for module-specific tags
|
|
308
|
+
// Format: moduleName@* where * matches any version
|
|
309
|
+
return `${moduleName}@*`;
|
|
310
|
+
}
|
|
311
|
+
/**
|
|
312
|
+
* Parses a git tag name to extract module and version components.
|
|
313
|
+
*
|
|
314
|
+
* This internal utility function handles multiple tag naming conventions used in
|
|
315
|
+
* μVERSE and returns a structured object with extracted metadata. It supports:
|
|
316
|
+
* - **Module tags**: `moduleName@version` (monorepo convention)
|
|
317
|
+
* - **Version tags**: `v{version}` or `{version}` (single repo convention)
|
|
318
|
+
* - **Custom tags**: Returns empty object for unrecognized formats
|
|
319
|
+
*
|
|
320
|
+
* @param tagName - The full git tag name to parse.
|
|
321
|
+
* Can be any string, but structured formats are recognized.
|
|
322
|
+
*
|
|
323
|
+
* @returns Object with optional `module` and `version` fields:
|
|
324
|
+
* - Both present: Module tag (e.g., `core@1.0.0`)
|
|
325
|
+
* - Only version: General tag (e.g., `v1.0.0`)
|
|
326
|
+
* - Empty object: Unrecognized format
|
|
327
|
+
* @internal
|
|
328
|
+
*/
|
|
329
|
+
function parseTagName(tagName) {
|
|
330
|
+
// Try to match module-specific tag pattern: moduleName@version
|
|
331
|
+
// Regex: ^(.+)@(.+)$
|
|
332
|
+
// ^(.+) - Start of string, capture group 1 (module name, greedy)
|
|
333
|
+
// @ - Literal @ separator
|
|
334
|
+
// (.+)$ - Capture group 2 (version, greedy), end of string
|
|
335
|
+
const match = tagName.match(/^(.+)@(.+)$/);
|
|
336
|
+
if (match) {
|
|
337
|
+
// Module tag matched - return both components
|
|
338
|
+
return {
|
|
339
|
+
module: match[1],
|
|
340
|
+
version: match[2],
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
// Try to match version-only tag pattern: v?MAJOR.MINOR.PATCH...
|
|
344
|
+
// Regex: ^v?(\d+\.\d+\.\d+.*)$
|
|
345
|
+
// ^v? - Start, optional 'v' prefix
|
|
346
|
+
// (\d+\.\d+\.\d+ - Capture group: MAJOR.MINOR.PATCH (digits)
|
|
347
|
+
// .*)$ - Any remaining characters (pre-release, metadata), end
|
|
348
|
+
const versionMatch = tagName.match(/^v?(\d+\.\d+\.\d+.*)$/);
|
|
349
|
+
if (versionMatch) {
|
|
350
|
+
// Version tag matched - return only version (no module)
|
|
351
|
+
return {
|
|
352
|
+
version: versionMatch[1],
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
// Unrecognized format - return empty object
|
|
356
|
+
return {};
|
|
357
|
+
}
|
|
358
|
+
/**
|
|
359
|
+
* Checks if the git working directory is clean (no uncommitted changes).
|
|
360
|
+
* Uses `git status --porcelain` to detect modified, staged, deleted, or untracked files.
|
|
361
|
+
* @param options - Git operation options
|
|
362
|
+
* @returns Promise resolving to true if clean, false if there are changes or on error
|
|
363
|
+
*/
|
|
364
|
+
export async function isWorkingDirectoryClean(options = {}) {
|
|
365
|
+
// Resolve working directory
|
|
366
|
+
const cwd = options.cwd || process.cwd();
|
|
367
|
+
try {
|
|
368
|
+
// Get machine-readable status output
|
|
369
|
+
// --porcelain: Stable, easy-to-parse format
|
|
370
|
+
const { stdout } = await execa("git", ["status", "--porcelain"], {
|
|
371
|
+
cwd,
|
|
372
|
+
});
|
|
373
|
+
// Empty output means clean working directory
|
|
374
|
+
// Any output indicates changes (modified, untracked, staged, etc.)
|
|
375
|
+
return stdout.trim() === "";
|
|
376
|
+
}
|
|
377
|
+
catch (error) {
|
|
378
|
+
// On error, assume directory is not clean (safe default)
|
|
379
|
+
// This could happen if not a git repo, or permissions issue
|
|
380
|
+
return false;
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
/**
|
|
384
|
+
* Retrieves the name of the currently checked out git branch.
|
|
385
|
+
*
|
|
386
|
+
* This function returns the active branch name, which is useful for:
|
|
387
|
+
* - Conditional logic based on branch (e.g., only release from 'main')
|
|
388
|
+
* - CI/CD branch-specific workflows
|
|
389
|
+
* - Logging and debugging
|
|
390
|
+
* - Branch validation before operations
|
|
391
|
+
*
|
|
392
|
+
* Returns empty string if in detached HEAD state.
|
|
393
|
+
* @param options - Git operation options
|
|
394
|
+
* @returns Promise resolving to the current branch name (empty string if detached HEAD)
|
|
395
|
+
* @throws {Error} If git command fails
|
|
396
|
+
*/
|
|
397
|
+
export async function getCurrentBranch(options = {}) {
|
|
398
|
+
// Resolve working directory
|
|
399
|
+
const cwd = options.cwd || process.cwd();
|
|
400
|
+
try {
|
|
401
|
+
// Get the current branch name
|
|
402
|
+
// --show-current: Returns active branch name or empty string if detached
|
|
403
|
+
const { stdout } = await execa("git", ["branch", "--show-current"], {
|
|
404
|
+
cwd,
|
|
405
|
+
});
|
|
406
|
+
// Return branch name (or empty string for detached HEAD)
|
|
407
|
+
return stdout.trim();
|
|
408
|
+
}
|
|
409
|
+
catch (error) {
|
|
410
|
+
// Wrap error with context
|
|
411
|
+
throw new Error(`Failed to get current branch: ${error}`);
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
/**
|
|
415
|
+
* Retrieves the abbreviated (short) SHA-1 hash of the current HEAD commit.
|
|
416
|
+
*
|
|
417
|
+
* This function returns a shortened version of the commit hash (typically 7 characters),
|
|
418
|
+
* which is:
|
|
419
|
+
* - Human-readable and easier to reference
|
|
420
|
+
* - Suitable for build metadata in semantic versions
|
|
421
|
+
* - Commonly used in CI/CD for build identification
|
|
422
|
+
* - Still unique enough for most repositories
|
|
423
|
+
*
|
|
424
|
+
* The short SHA is git's default abbreviated format and balances uniqueness with brevity.
|
|
425
|
+
*
|
|
426
|
+
* @param options - Git operation options, primarily for specifying working directory.
|
|
427
|
+
*
|
|
428
|
+
* @returns Promise resolving to the abbreviated commit SHA.
|
|
429
|
+
* Typically 7 characters (e.g., 'abc1234').
|
|
430
|
+
* Length may vary based on repository size to ensure uniqueness.
|
|
431
|
+
*
|
|
432
|
+
* @throws {Error} If git command fails:
|
|
433
|
+
* - Not in a git repository
|
|
434
|
+
* - No commits exist (empty repository)
|
|
435
|
+
* - Permissions issues
|
|
436
|
+
*/
|
|
437
|
+
export async function getCurrentCommitShortSha(options = {}) {
|
|
438
|
+
// Resolve working directory
|
|
439
|
+
const cwd = options.cwd || process.cwd();
|
|
440
|
+
try {
|
|
441
|
+
// Get abbreviated commit SHA
|
|
442
|
+
// rev-parse: Resolve git revision to commit hash
|
|
443
|
+
// --short: Return abbreviated version (typically 7 chars)
|
|
444
|
+
// HEAD: The current commit
|
|
445
|
+
const { stdout } = await execa("git", ["rev-parse", "--short", "HEAD"], {
|
|
446
|
+
cwd,
|
|
447
|
+
});
|
|
448
|
+
// Return the short SHA
|
|
449
|
+
return stdout.trim();
|
|
450
|
+
}
|
|
451
|
+
catch (error) {
|
|
452
|
+
// Wrap error with context
|
|
453
|
+
throw new Error(`Failed to get current commit SHA: ${error}`);
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
/**
|
|
457
|
+
* Stages all changed files in the working directory for the next commit.
|
|
458
|
+
*
|
|
459
|
+
* This function executes `git add .` which stages:
|
|
460
|
+
* - All modified tracked files
|
|
461
|
+
* - All new untracked files
|
|
462
|
+
* - All deleted files
|
|
463
|
+
*
|
|
464
|
+
* **Warning**: This stages **everything** in the working directory. Use with caution
|
|
465
|
+
* in interactive environments. For selective staging, use git commands directly.
|
|
466
|
+
*
|
|
467
|
+
* @param options - Git operation options, primarily for specifying working directory.
|
|
468
|
+
*
|
|
469
|
+
* @returns Promise that resolves when all files are successfully staged.
|
|
470
|
+
*
|
|
471
|
+
* @throws {Error} If git add command fails:
|
|
472
|
+
* - Not in a git repository
|
|
473
|
+
* - Permissions issues
|
|
474
|
+
* - Invalid .gitignore patterns
|
|
475
|
+
*/
|
|
476
|
+
export async function addChangedFiles(options = {}) {
|
|
477
|
+
// Resolve working directory
|
|
478
|
+
const cwd = options.cwd || process.cwd();
|
|
479
|
+
try {
|
|
480
|
+
// Stage all changes in the working directory
|
|
481
|
+
// '.': Current directory and all subdirectories
|
|
482
|
+
await execa("git", ["add", "."], { cwd });
|
|
483
|
+
}
|
|
484
|
+
catch (error) {
|
|
485
|
+
// Wrap error with context
|
|
486
|
+
throw new Error(`Failed to add changed files: ${error}`);
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
/**
|
|
490
|
+
* Creates a git commit with the specified message using currently staged changes.
|
|
491
|
+
* Files must be staged first (via `git add`). Follows Conventional Commits format.
|
|
492
|
+
* @param message - The commit message (e.g., 'feat: description', 'fix: description')
|
|
493
|
+
* @param options - Git operation options
|
|
494
|
+
* @returns Promise that resolves when commit is created
|
|
495
|
+
* @throws {Error} If commit fails (no staged changes, no git user, empty message, etc.)
|
|
496
|
+
*/
|
|
497
|
+
export async function commitChanges(message, options = {}) {
|
|
498
|
+
// Resolve working directory
|
|
499
|
+
const cwd = options.cwd || process.cwd();
|
|
500
|
+
try {
|
|
501
|
+
// Create commit with staged changes
|
|
502
|
+
// -m: Specify commit message inline
|
|
503
|
+
await execa("git", ["commit", "-m", message], { cwd });
|
|
504
|
+
}
|
|
505
|
+
catch (error) {
|
|
506
|
+
// Wrap error with context
|
|
507
|
+
throw new Error(`Failed to commit changes: ${error}`);
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
/**
|
|
511
|
+
* Pushes local commits to the remote repository.
|
|
512
|
+
*
|
|
513
|
+
* This function uploads all commits from the current branch that don't exist
|
|
514
|
+
* on the remote. It uses `git push` without arguments, which:
|
|
515
|
+
* - Pushes the current branch to its configured upstream
|
|
516
|
+
* - Only pushes commits (use `pushTags()` for tags)
|
|
517
|
+
* - Requires network access and authentication
|
|
518
|
+
*
|
|
519
|
+
* @param options - Git operation options, primarily for specifying working directory.
|
|
520
|
+
*
|
|
521
|
+
* @returns Promise that resolves when commits are successfully pushed.
|
|
522
|
+
*
|
|
523
|
+
* @throws {Error} If push fails:
|
|
524
|
+
* - No remote configured
|
|
525
|
+
* - No upstream branch set
|
|
526
|
+
* - Authentication failure
|
|
527
|
+
* - Network issues
|
|
528
|
+
* - Remote rejects (e.g., force push needed, protected branch)
|
|
529
|
+
*/
|
|
530
|
+
export async function pushCommits(options = {}) {
|
|
531
|
+
// Resolve working directory
|
|
532
|
+
const cwd = options.cwd || process.cwd();
|
|
533
|
+
try {
|
|
534
|
+
// Push commits to remote
|
|
535
|
+
// No arguments: Push current branch to configured upstream
|
|
536
|
+
await execa("git", ["push"], { cwd });
|
|
537
|
+
}
|
|
538
|
+
catch (error) {
|
|
539
|
+
// Wrap error with context
|
|
540
|
+
throw new Error(`Failed to push commits: ${error}`);
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
/**
|
|
544
|
+
* Checks if there are any changes in the working directory or staging area.
|
|
545
|
+
*
|
|
546
|
+
* This function is similar to `isWorkingDirectoryClean()` but returns the opposite
|
|
547
|
+
* boolean value. It's useful when you want to check if there's work to commit.
|
|
548
|
+
*
|
|
549
|
+
* Uses `git status --porcelain` to detect:
|
|
550
|
+
* - Modified tracked files
|
|
551
|
+
* - New untracked files
|
|
552
|
+
* - Deleted files
|
|
553
|
+
* - Staged changes
|
|
554
|
+
*
|
|
555
|
+
* @param options - Git operation options, primarily for specifying working directory.
|
|
556
|
+
*
|
|
557
|
+
* @returns Promise resolving to:
|
|
558
|
+
* - `true`: There are changes (modified, staged, untracked files)
|
|
559
|
+
* - `false`: Working directory is clean OR git command failed
|
|
560
|
+
*
|
|
561
|
+
* @throws {Error} If git status command fails.
|
|
562
|
+
* Unlike `isWorkingDirectoryClean()`, this function throws on errors.
|
|
563
|
+
*/
|
|
564
|
+
export async function hasChangesToCommit(options = {}) {
|
|
565
|
+
// Resolve working directory
|
|
566
|
+
const cwd = options.cwd || process.cwd();
|
|
567
|
+
try {
|
|
568
|
+
// Get machine-readable status output
|
|
569
|
+
// --porcelain: Stable, easy-to-parse format
|
|
570
|
+
const { stdout } = await execa("git", ["status", "--porcelain"], {
|
|
571
|
+
cwd,
|
|
572
|
+
});
|
|
573
|
+
// If output is not empty, there are changes
|
|
574
|
+
// Returns true if changes exist, false if clean
|
|
575
|
+
return stdout.trim().length > 0;
|
|
576
|
+
}
|
|
577
|
+
catch (error) {
|
|
578
|
+
// Throw on error (unlike isWorkingDirectoryClean which returns false)
|
|
579
|
+
throw new Error(`Failed to check git status: ${error}`);
|
|
580
|
+
}
|
|
581
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export * from './config/index.js';
|
|
2
|
+
export { VerseRunner } from './services/verse-runner.js';
|
|
3
|
+
export type { RunnerOptions, RunnerResult } from './services/verse-runner.js';
|
|
4
|
+
export { VersionManager } from './services/version-manager.js';
|
|
5
|
+
export { ModuleRegistry } from './services/module-registry.js';
|
|
6
|
+
export { VersionBumper } from './services/version-bumper.js';
|
|
7
|
+
export type { VersionBumperOptions } from './services/version-bumper.js';
|
|
8
|
+
export { VersionApplier } from './services/version-applier.js';
|
|
9
|
+
export type { VersionApplierOptions, ModuleChangeResult } from './services/version-applier.js';
|
|
10
|
+
export { ChangelogGenerator } from './services/changelog-generator.js';
|
|
11
|
+
export { GitOperations } from './services/git-operations.js';
|
|
12
|
+
export type { GitOperationsOptions } from './services/git-operations.js';
|
|
13
|
+
export { CommitAnalyzer } from './services/commit-analyzer.js';
|
|
14
|
+
export { ConfigurationLoader } from './services/configuration-loader.js';
|
|
15
|
+
export type { AdapterIdentifier, AdapterMetadata } from './services/adapter-identifier.js';
|
|
16
|
+
export { AdapterIdentifierRegistry } from './services/adapter-identifier-registry.js';
|
|
17
|
+
export type { ProjectInformation, Module } from './adapters/project-information.js';
|
|
18
|
+
export { createModuleSystemFactory } from './factories/module-system-factory.js';
|
|
19
|
+
export type { ModuleSystemFactory } from './services/module-system-factory.js';
|
|
20
|
+
export * from './git/index.js';
|
|
21
|
+
export * from './semver/index.js';
|
|
22
|
+
export * from './utils/index.js';
|
|
23
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,cAAc,mBAAmB,CAAC;AAGlC,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AACzD,YAAY,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC9E,OAAO,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAC/D,OAAO,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAC/D,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAC7D,YAAY,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AACzE,OAAO,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAC/D,YAAY,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AAC/F,OAAO,EAAE,kBAAkB,EAAE,MAAM,mCAAmC,CAAC;AACvE,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAC7D,YAAY,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AACzE,OAAO,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAC/D,OAAO,EAAE,mBAAmB,EAAE,MAAM,oCAAoC,CAAC;AACzE,YAAY,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AAC3F,OAAO,EAAE,yBAAyB,EAAE,MAAM,2CAA2C,CAAC;AAGtF,YAAY,EAAE,kBAAkB,EAAE,MAAM,EAAE,MAAM,mCAAmC,CAAC;AAGpF,OAAO,EAAE,yBAAyB,EAAE,MAAM,sCAAsC,CAAC;AACjF,YAAY,EAAE,mBAAmB,EAAE,MAAM,qCAAqC,CAAC;AAG/E,cAAc,gBAAgB,CAAC;AAG/B,cAAc,mBAAmB,CAAC;AAGlC,cAAc,kBAAkB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// Core μVERSE exports - business logic without GitHub Actions dependency
|
|
2
|
+
// Configuration
|
|
3
|
+
export * from './config/index.js';
|
|
4
|
+
// Services
|
|
5
|
+
export { VerseRunner } from './services/verse-runner.js';
|
|
6
|
+
export { VersionManager } from './services/version-manager.js';
|
|
7
|
+
export { ModuleRegistry } from './services/module-registry.js';
|
|
8
|
+
export { VersionBumper } from './services/version-bumper.js';
|
|
9
|
+
export { VersionApplier } from './services/version-applier.js';
|
|
10
|
+
export { ChangelogGenerator } from './services/changelog-generator.js';
|
|
11
|
+
export { GitOperations } from './services/git-operations.js';
|
|
12
|
+
export { CommitAnalyzer } from './services/commit-analyzer.js';
|
|
13
|
+
export { ConfigurationLoader } from './services/configuration-loader.js';
|
|
14
|
+
export { AdapterIdentifierRegistry } from './services/adapter-identifier-registry.js';
|
|
15
|
+
// Factories
|
|
16
|
+
export { createModuleSystemFactory } from './factories/module-system-factory.js';
|
|
17
|
+
// Git utilities
|
|
18
|
+
export * from './git/index.js';
|
|
19
|
+
// Semver utilities
|
|
20
|
+
export * from './semver/index.js';
|
|
21
|
+
// Utilities
|
|
22
|
+
export * from './utils/index.js';
|