Version Management
This document describes version management in Structyl.
Overview
Structyl maintains a single version for the entire project. All language implementations share this version, ensuring consistency across packages.
Version Source
The canonical version is stored in a single file:
VERSIONContents (plain text, no newline required):
1.2.3Leading and trailing whitespace (including newlines) is stripped before parsing.
Version Format
Structyl expects Semantic Versioning:
MAJOR.MINOR.PATCH[-PRERELEASE][+BUILD]Examples:
1.0.02.1.31.0.0-alpha1.0.0-beta.22.0.0-rc.1+build.123
Error Conditions
| Condition | Exit Code | Error Message |
|---|---|---|
| Version source file missing | 2 | version source file not found: {path} |
| Version source file empty | 2 | version source file is empty: {path} |
| Invalid version format | 2 | invalid version format in {path}: "{content}" |
| Version file not readable | 3 | cannot read version file: {path}: {error} |
Version Commands
Get Current Version
structyl version
# Output: 1.2.3Set Version
structyl version set 2.0.0This:
- Updates the VERSION file
- Propagates to all configured files
- Regenerates documentation (if configured)
Bump Version
structyl version bump patch # 1.2.3 → 1.2.4
structyl version bump minor # 1.2.3 → 1.3.0
structyl version bump major # 1.2.3 → 2.0.0Prerelease Versions
structyl version set 2.0.0-alpha.1
structyl version bump prerelease # 2.0.0-alpha.1 → 2.0.0-alpha.2Prerelease Bump Edge Cases
| Current Version | After bump prerelease | Notes |
|---|---|---|
1.0.0 | Error | Cannot bump prerelease on release version |
1.0.0-alpha | 1.0.0-alpha.1 | Adds .1 suffix |
1.0.0-alpha.1 | 1.0.0-alpha.2 | Increments numeric suffix |
1.0.0-alpha.9 | 1.0.0-alpha.10 | No digit limit |
1.0.0-rc.1 | 1.0.0-rc.2 | Works with any prerelease tag |
1.0.0-beta.2+build.5 | 1.0.0-beta.3+build.5 | Build metadata preserved |
Error case:
structyl version bump prerelease # When VERSION contains "1.0.0"
# Error: cannot bump prerelease on release version "1.0.0"
# Exit code: 2Version Propagation
Structyl updates version strings in language-specific files using regex patterns.
Regex Syntax
Patterns use RE2 syntax (Go's standard regex engine). Notable characteristics:
.*?performs non-greedy matching[\s\S]matches any character including newlines- Capture groups use
$1,$2, etc. in replacement strings - No backreferences within patterns
- No lookahead or lookbehind assertions
Patterns are matched against the entire file content. Use anchors or capture groups to avoid unintended matches (e.g., matching dependency versions instead of package versions).
Match Cardinality
By default, each pattern MUST match exactly once per file:
| Matches Found | Behavior | Exit Code |
|---|---|---|
| 0 | Error: pattern not found in {path} | 2 |
| 1 | Replace the match | 0 |
| >1 | Error: pattern matched {n} times in {path} (expected 1) | 2 |
To replace all occurrences, set replace_all: true:
{
"path": "docs/version.txt",
"pattern": "v\\d+\\.\\d+\\.\\d+",
"replace": "v{version}",
"replace_all": true
}With replace_all: true:
| Matches Found | Behavior | Exit Code |
|---|---|---|
| 0 | Error: pattern not found in {path} | 2 |
| ≥1 | Replace all matches | 0 |
Configuration
{
"version": {
"source": "VERSION",
"files": [
{
"path": "cs/Directory.Build.props",
"pattern": "<Version>.*?</Version>",
"replace": "<Version>{version}</Version>"
},
{
"path": "py/pyproject.toml",
"pattern": "version = \".*?\"",
"replace": "version = \"{version}\""
}
]
}
}Fields
| Field | Description |
|---|---|
path | File path relative to project root |
pattern | Regex pattern to match version string |
replace | Replacement with {version} placeholder |
Pattern Examples
C# (Directory.Build.props)
<Version>1.2.3</Version>{
"path": "cs/Directory.Build.props",
"pattern": "<Version>.*?</Version>",
"replace": "<Version>{version}</Version>"
}Python (pyproject.toml)
version = "1.2.3"{
"path": "py/pyproject.toml",
"pattern": "version = \".*?\"",
"replace": "version = \"{version}\""
}Python (init.py)
__version__ = "1.2.3"{
"path": "py/mypackage/__init__.py",
"pattern": "__version__ = \".*?\"",
"replace": "__version__ = \"{version}\""
}Rust (Cargo.toml)
[package]
name = "mypackage"
version = "1.2.3"{
"path": "rs/mypackage/Cargo.toml",
"pattern": "(name = \"mypackage\"[\\s\\S]*?)version = \".*?\"",
"replace": "$1version = \"{version}\""
}Note: Rust pattern uses capture group to avoid matching dependency versions.
TypeScript (package.json)
{
"name": "mypackage",
"version": "1.2.3"
}{
"path": "ts/package.json",
"pattern": "\"version\": \".*?\"",
"replace": "\"version\": \"{version}\""
}Go (go.mod)
Go uses git tags for versioning. No file modification needed, but you can update a constant:
const Version = "1.2.3"{
"path": "go/version.go",
"pattern": "const Version = \".*?\"",
"replace": "const Version = \"{version}\""
}Kotlin (build.gradle.kts)
version = "1.2.3"{
"path": "kt/build.gradle.kts",
"pattern": "version = \".*?\"",
"replace": "version = \"{version}\""
}R (DESCRIPTION)
Version: 1.2.3{
"path": "r/mypackage/DESCRIPTION",
"pattern": "Version: .*",
"replace": "Version: {version}"
}Release Workflow
Manual Release
# 1. Set version
structyl version set 2.0.0
# 2. Review changes
git diff
# 3. Commit
git add -A
git commit -m "Release v2.0.0"
# 4. Tag
git tag v2.0.0
# 5. Push
git push origin main --tagsAutomated Release Command
structyl release 2.0.0 [--push]This command:
- Sets version in VERSION file
- Propagates version to all files
- Regenerates documentation
- Creates git commit:
"Release v2.0.0" - Creates git tag:
v2.0.0 - (with
--push) Pushes tooriginremote
The --push flag always pushes to the origin remote. To push to a different remote, use manual git commands after structyl release.
Go Module Tag
Go modules require a special tag format. Configure additional tags:
structyl release 2.0.0 --push
# Creates: v2.0.0, go/v2.0.0Validation
Check Version Consistency
structyl version checkVerifies all configured files contain the expected version:
VERSION: 2.0.0
cs/Directory.Build.props: 2.0.0 ✓
py/pyproject.toml: 2.0.0 ✓
rs/mypackage/Cargo.toml: 1.9.0 ✗ (expected 2.0.0)Exit code 1 if any mismatch found. This is a runtime check of project state (not a configuration error), consistent with exit code 1 semantics for "expected runtime failure."
Configuration Reference
{
"version": {
"source": "VERSION",
"files": [
{
"path": "path/to/file",
"pattern": "regex pattern",
"replace": "replacement with {version}",
"replace_all": false
}
]
}
}| Field | Default | Description |
|---|---|---|
source | "VERSION" | Version file path |
files | [] | Files to update |
files[].path | Required | File path |
files[].pattern | Required | Regex to match |
files[].replace | Required | Replacement string |
files[].replace_all | false | Replace all matches (vs. require exactly one) |
CLI Version Pinning
Each Structyl project pins the CLI version in .structyl/version. This ensures reproducible builds across different machines and CI environments.
Version File
The .structyl/version file contains a single line with the pinned CLI version:
1.2.3This file is created by structyl init and should be committed to version control.
Upgrade Command
Usage: structyl upgrade [version] [--check]
structyl upgrade --check| Command | Description |
|---|---|
structyl upgrade | Upgrade to latest stable version |
structyl upgrade <version> | Upgrade to specific version (e.g., 1.2.3, nightly) |
structyl upgrade --check | Show current vs latest version without changing |
Version Types
| Type | Validation | Cache Check | Install Prompt |
|---|---|---|---|
Stable (e.g., 1.2.3) | Semver validation | Check ~/.structyl/versions/ | Only if not installed |
| Nightly | Skip validation | Skip | Always prompt |
GitHub API Integration
The upgrade command fetches the latest version from:
https://api.github.com/repos/AndreyAkinshin/structyl/releases/latestResponse parsing:
- Extract
tag_namefield - Strip
vprefix (e.g.,v1.2.3→1.2.3) - 10-second HTTP timeout
- User-Agent header required
Exit Codes
| Code | Meaning |
|---|---|
| 0 | Success |
| 1 | Runtime error (network failure, file I/O, not in project) |
| 2 | Usage error (invalid version format, unknown flag) |
Output Examples
Upgrade to latest:
Upgraded from 1.1.0 to 1.2.3
Run '.structyl/setup.sh' to install version 1.2.3.Check mode:
Current CLI version: 1.2.0
Pinned version: 1.1.0
Latest stable: 1.2.3
A newer version is available. Run 'structyl upgrade' to update.Nightly upgrade:
Upgraded from 1.1.0 to nightly
Run '.structyl/setup.sh' to install the nightly build.Already on version:
Already on version 1.2.3