Workspace - Command-Line Tool Suite

Workspace is a tool suite for file operations, version management, and development workflow automation. Includes refactor (string replacement), scrap (local trash folder), unscrap (file restoration), and state (automatic versioning).

View on GitHub

State Guide

State is the version management component of Workspace. It integrates with Git to provide automatic semantic versioning, template rendering, and cross-project version stamping via .wstemplate files.

How Version Calculation Works

State uses a three-part versioning scheme:

Final Version Format: {major}.{minor}.{patch}

Example

# Repository state:
# - Major version set to 2 (via wsb version major 2)
# - Total commits: 150
# - Total line changes: 4200

# Result: 2.150.4200

Installation and Setup

1. Install Workspace

./install.sh

2. Inspect Git State

Navigate to your Git repository and inspect the active git scope:

cd your-git-repo
wsb status

3. Configure (Optional)

Create a .state.json configuration file in your repository root:

{
  "version": 1,
  "enabled": true,
  "version_file": "version.txt"
}

Configuration Options:

Basic Usage

Git Status

wsb status

Show Version Information

wsb version show
wsb version show --verbose
wsb version show --format json

Manual Version Update

wsb update                 # Update version and render templates
wsb update --git-add       # Also stage changed files
wsb update --no-git        # Skip git integration

Set Major Version

wsb version major 1        # Set major to 1
wsb version major 2        # Bump major to 2

Create Git Tag

wsb version tag                    # Tag with current version
wsb version tag --prefix "release-"  # Custom prefix
wsb version tag --message "Release"  # With message

Check Status

wsb status

Workflow Integration

  1. Work normally in your repository or fleet root
  2. Run wsb update before commit boundaries when you want version files refreshed
  3. Version files are updated when you run wsb update:
    • version.txt (or custom version file)
    • All detected project files (Cargo.toml, package.json, etc.)
    • All .wstemplate files relevant to this project
  4. Updated output files are automatically staged (version.txt, rendered templates)

Note: The .wsb directory (containing state.json, logs, databases) is local project state and should be in .gitignore. It is never staged by wsb update.

Manual Mode

  1. Set "enabled": false in .state.json
  2. Run wsb update when needed
  3. Use wsb update --git-add to stage updated files
  4. Commit version changes manually

Project File Auto-Detection

State automatically detects and updates version fields in common project files:

Supported File Types:

Disable Auto-Detection:

{
  "auto_detect_project_files": false
}

Template System (.tera)

Workspace supports Tera templates for generating files with version information.

Managing Templates

wsb template add version-header \
  --template "Version: " \
  --output version.h

wsb template list
wsb template show version-header
wsb template render
wsb template delete version-header

Templates are rendered automatically during wsb update.

Wstemplate System (.wstemplate)

The wstemplate system provides cross-project version stamping. A .wstemplate file is a Tera template that renders to the file with the .wstemplate suffix stripped.

Single-Entry Model

Each project has at most one wstemplate entry in its .wsb/state.json:

Cross-project references are resolved dynamically — no explicit cross-project entries are needed.

Setup

# Register this project with the wstemplate system
wsb wstemplate add /path/to/workspace

# Verify the entry
wsb wstemplate list-entries
# Output:
# 1 wstemplate entries:
#   my_project -> /path/to/workspace

The alias is auto-derived from the project directory name:

Override with --alias:

wsb wstemplate add /path/to/workspace --alias mylib

Template Variables

describes the project that owns the template file, and exposes the same fields for any project in the workspace — submodules included, since each is a project in its own right.

Variable Description
`` Project name
`` Absolute path to the project root
`` Path from the workspace root, e.g. ext/gtest
`` Full version being built, from version.txt
`` e.g., v0
`` Commit count, as a number
`` Line changes, as a number
`` Highest tag in this clone, e.g. v1.648.6494
`` That tag without its v — the pin-ready form
`` Newest tag reachable from HEAD
`` Number of tags, as a number
`` Highest tag on the remote (contacts the network)
`` That tag without its v (contacts the network)
`` Full HEAD commit hash
`` Abbreviated HEAD commit hash
`` HEAD commit date, YYYY-MM-DD
`` HEAD commit timestamp, RFC 3339
`` Checked-out branch
`` Whether HEAD is detached, as a boolean
`` Whether the worktree is modified, as a boolean
`` Remote the project publishes to
`` Fetch URL of that remote
`` RFC 3339 timestamp
`` YYYY-MM-DD
`` HH:MM:SS
`` Year
`` Month
`` Day
`` Seconds since the Unix epoch, as a number

wsb wstemplate vars prints this table with the values resolved for a real project, which is the fastest way to see what a template will actually stamp.

version versus latest_version

version is the version being produced — version.txt is bumped on commit, so it sits one step ahead of the newest published tag. That is correct for a dependency resolved from a local checkout inside the workspace, and wrong for a pin that somebody outside the workspace has to resolve from a registry: that pin names a release which does not exist yet. Use latest_version there.

latest_tag reflects the tags in this clone. latest_remote_tag asks the remote directly and is unaffected by how stale the clone is; those two fields are the only ones that touch the network, and only when a template references them.

Fields That Do Not Apply

A field is either a real value or an error naming the reason — never an empty string. A project with no tags has no latest_version, and a template asking for one fails with “it has no tags” rather than writing "" into a manifest. Every reference is checked before rendering starts, so a template that cannot resolve leaves no half-written output behind.

Example: Cargo.toml.wstemplate

[package]
name = "my-app"
version = ""

[dependencies]
my-lib = { path = "../my-lib", version = "" }

When wsb update runs in this project (or in my-lib), this template renders to Cargo.toml with actual version numbers.

Example: package.json.wstemplate

{
  "name": "@scope/my-app",
  "version": "",
  "dependencies": {
    "@scope/tagged-urn": ""
  }
}

How Template Selection Works

When wsb update (or wsb wstemplate render) runs for project X:

  1. All .wstemplate files in the scan root are discovered
  2. A template is rendered if:
    • It lives under project X’s root (own templates), OR
    • Its text contains `` (references to X)
  3. For each template, `` resolves to the owning project
  4. `` resolves dynamically: the alias’s version.txt for the version fields, and its git repository for the tag and head fields

Dynamic Cross-Project Resolution

The engine discovers all projects by scanning for .wsb/state.json files in the scan root. When a template references ``:

  1. The engine finds other_lib’s project root from its state.json
  2. Reads {project_root}/version.txt
  3. Parses the version and populates the template variable

Error Handling

All errors are hard failures — no silent fallbacks:

Batch Setup Script

For a multi-project workspace, create a setup script:

#!/usr/bin/env bash
set -euo pipefail
ROOT="$(cd "$(dirname "$0")" && pwd)"

register() {
    local project="$1"
    echo "=== $project ==="
    cd "$ROOT/$project"
    wsb wstemplate add "$ROOT"
    echo
}

register my-lib
register my-app
register my-tests

echo "Done. Run 'wsb update' in any project to render its templates."

Wstemplate Commands

wsb wstemplate add <PATH> [--alias <ALIAS>]  # Set scan root
wsb wstemplate remove <ALIAS>                 # Remove entry
wsb wstemplate list-entries                    # Show this project's entry
wsb wstemplate list                            # List relevant templates
wsb wstemplate render                          # Render all relevant templates

Troubleshooting

Hook Not Running

  1. Check repository status: wsb status
  2. Run wsb update
  3. Verify the changed files with git status

Version Not Updating

  1. Check git repository status: git status
  2. Inspect version state: wsb version show
  3. Run wsb update

Wstemplate Not Rendering

  1. Check entry exists: wsb wstemplate list-entries
  2. Check for relevant templates: wsb wstemplate list
  3. Try explicit render: wsb wstemplate render
  4. Check that dependency projects have version.txt (run wsb update in them)

Removing Version Management

rm .state.json            # Remove configuration (optional)
rm version.txt          # Remove version file (optional)
wsb wstemplate remove my_alias  # Remove wstemplate entry (optional)

Logging

State logs all actions to .wsb/logs/wsb.log:

tail -f .wsb/logs/wsb.log

Best Practices

  1. Install Early: Set up versioning when creating a new repository
  2. Use wsb version major: Set major version via database, not git tags
  3. Consistent Workflow: Let the hook handle versioning automatically
  4. Shared Scan Root: For multi-project workspaces, point all projects’ wstemplate to the common root
  5. Run wsb update After Setup: Ensure version.txt exists before other projects reference it
  6. Add .wsb to .gitignore: The .wsb directory is local state — do not commit it

Integration Examples

CI/CD Pipeline

VERSION=$(cat version.txt)
echo "Building version: $VERSION"
docker build -t myapp:$VERSION .

GitHub Actions

- name: Get Version
  id: version
  run: echo "version=$(cat version.txt)" >> $GITHUB_OUTPUT

- name: Create Release
  uses: actions/create-release@v1
  with:
    tag_name: v$
    release_name: Release $

Makefile Integration

VERSION := $(shell cat version.txt)

build:
	@echo "Building version $(VERSION)"
	cargo build --release

See Also