Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 109 additions & 0 deletions .github/workflows/validate-version-bump.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
---
name: "Validate Version Bump"
on:
pull_request:
paths:
- 'src/**/devcontainer-feature.json'

jobs:
validate-version-bump:
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write

steps:
- name: Checkout PR branch
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Validate version changes
run: |
set -e

echo "Checking for version changes..."

# Get changed files
CHANGED_FILES=$(git diff --name-only \
origin/${{ github.base_ref }}...HEAD | \
grep 'devcontainer-feature.json$' || true)

if [ -z "$CHANGED_FILES" ]; then
echo "No devcontainer-feature.json files changed"
exit 0
fi

HAS_ERROR=0

for FILE in $CHANGED_FILES; do
echo ""
echo "Checking $FILE..."

# Get old version
OLD_VERSION=$(git show \
origin/${{ github.base_ref }}:$FILE | \
jq -r '.version' 2>/dev/null || echo "")
NEW_VERSION=$(jq -r '.version' $FILE 2>/dev/null || echo "")
FEATURE_ID=$(jq -r '.id' $FILE 2>/dev/null || echo "unknown")

if [ -z "$OLD_VERSION" ]; then
echo " New feature: $FEATURE_ID (version: $NEW_VERSION)"
continue
fi

if [ -z "$NEW_VERSION" ]; then
echo " Error: Could not read version from $FILE"
HAS_ERROR=1
continue
fi

# Validate semantic versioning format
if ! echo "$NEW_VERSION" | \
grep -qE '^[0-9]+\.[0-9]+\.[0-9]+$'; then
echo " Error: Version does not follow semver"
HAS_ERROR=1
continue
fi

if [ "$OLD_VERSION" = "$NEW_VERSION" ]; then
echo " Warning: Version unchanged ($OLD_VERSION)"
else
echo " Version bumped: $OLD_VERSION -> $NEW_VERSION"

# Parse versions
OLD_MAJOR=$(echo $OLD_VERSION | cut -d. -f1)
OLD_MINOR=$(echo $OLD_VERSION | cut -d. -f2)
OLD_PATCH=$(echo $OLD_VERSION | cut -d. -f3)

NEW_MAJOR=$(echo $NEW_VERSION | cut -d. -f1)
NEW_MINOR=$(echo $NEW_VERSION | cut -d. -f2)
NEW_PATCH=$(echo $NEW_VERSION | cut -d. -f3)

# Check if version increased
if [ $NEW_MAJOR -lt $OLD_MAJOR ]; then
echo " Error: Major version decreased"
HAS_ERROR=1
elif [ $NEW_MAJOR -eq $OLD_MAJOR ]; then
if [ $NEW_MINOR -lt $OLD_MINOR ]; then
echo " Error: Minor version decreased"
HAS_ERROR=1
elif [ $NEW_MINOR -eq $OLD_MINOR ]; then
if [ $NEW_PATCH -lt $OLD_PATCH ]; then
echo " Error: Patch version decreased"
HAS_ERROR=1
fi
fi
fi
fi
done

if [ $HAS_ERROR -eq 1 ]; then
echo ""
echo "Version validation failed"
exit 1
fi

echo ""
echo "All version changes are valid"

8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -174,3 +174,11 @@ dist
# Finder (MacOS) folder config
.DS_Store
.direnv

# Python
__pycache__/
*.py[cod]
*$py.class
*.so
.Python

6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,9 @@ A collection of devcontainer features.
| [powerlevel10k](./src/powerlevel10k) | PowerLevel10k is a theme for Zsh. |
| [rye](./src/rye) | A Hassle-Free Python Experience. |
| [starship](./src/starship) | The minimal, blazing-fast, and infinitely customizable prompt for any shell! |

## Development

### Version Bumping

See [docs/VERSION_BUMPING.md](./docs/VERSION_BUMPING.md) for information on bumping feature versions.
120 changes: 120 additions & 0 deletions docs/NUSHELL_INC_COMMAND.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
# Note on Nushell's `inc` Command

## Background

The Nushell documentation references an `inc` command that was designed to increment values, including semantic version strings:
- Documentation: https://www.nushell.sh/commands/docs/inc.html
- Supported flags: `--major`, `--minor`, `--patch`

## Current Status

The `inc` command was available in Nushell versions prior to 0.80 but has been **moved to a plugin** in current versions (0.107.0+).

### nu_plugin_inc

The `inc` functionality is now available as an official Nushell plugin: `nu_plugin_inc`

## Installation

### Step 1: Install the Plugin

```bash
cargo install nu_plugin_inc
```

### Step 2: Register the Plugin with Nushell

```bash
# Start nushell
nu

# Register the plugin
plugin add ~/.cargo/bin/nu_plugin_inc

# Or in one command:
nu -c "plugin add ~/.cargo/bin/nu_plugin_inc"
```

### Step 3: Verify Installation

```bash
nu -c '"1.2.3" | inc --patch'
# Should output: 1.2.4
```

## Implementation in bump-version.nu

The `scripts/bump-version.nu` script now uses a **hybrid approach**:

1. **First, try to use the `inc` plugin** if it's installed and registered
2. **Fallback to custom implementation** if the plugin is not available

This ensures the script works whether or not the plugin is installed, while preferring the official plugin when available.

### Code Implementation:

```nushell
def bump_semver [
version: string
bump_type: string
]: nothing -> string {
# Try to use inc plugin if available
let inc_result = try {
match $bump_type {
"major" => { $version | inc --major }
"minor" => { $version | inc --minor }
"patch" => { $version | inc --patch }
}
} catch {
null
}

# If inc plugin worked, return the result
if $inc_result != null {
return $inc_result
}

# Fallback to custom implementation
# [custom implementation code]
}
```

## Benefits

1. **Plugin-first approach**: Uses official nu_plugin_inc when available
2. **Graceful fallback**: Works without the plugin for compatibility
3. **No manual configuration**: Script detects and uses the plugin automatically
4. **Best of both worlds**: Official plugin + guaranteed functionality

## Advantages of Using the Plugin

When the plugin is installed:
- ✅ Uses official, maintained code from the Nushell team
- ✅ Consistent with Nushell ecosystem
- ✅ Automatically updated with plugin updates
- ✅ Better integration with Nushell's type system

## Compatibility

- **With plugin**: Uses `nu_plugin_inc` (recommended)
- **Without plugin**: Uses custom implementation (automatic fallback)
- **Both approaches**: Produce identical results

## Setup in CI/CD

To use the plugin in CI/CD environments, add to your setup:

```yaml
- name: Install nu_plugin_inc
run: |
cargo install nu_plugin_inc
nu -c "plugin add ~/.cargo/bin/nu_plugin_inc"
```

## Conclusion

The script now follows best practices by:
1. Preferring the official `nu_plugin_inc` when available
2. Providing a fallback for environments without the plugin
3. Automatically detecting and using the appropriate method
4. Requiring no changes to the command-line interface
112 changes: 112 additions & 0 deletions docs/NUSHELL_RATIONALE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
# Nushell vs Python: Version Bumping Implementation

This document explains why Nushell was chosen as the primary scripting language for version bumping.

## Comparison

### Native JSON Support

**Python:**
```python
import json

with open(json_path, 'r') as f:
data = json.load(f)

# Modify data
data['version'] = new_version

with open(json_path, 'w') as f:
json.dump(data, f, indent=4)
```

**Nushell:**
```nushell
let data = open $json_path
let updated_data = ($data | upsert version $new_version)
$updated_data | to json --indent 4 | save --force $json_path
```

### Structured Data Handling

Nushell treats JSON, CSV, and other structured data as first-class citizens, making data manipulation more natural and less error-prone.

### Type Safety

**Python:**
- Dynamic typing with potential runtime errors
- Manual type checking needed
- Error handling via try/except

**Nushell:**
- Strongly typed with better compile-time checks
- Built-in error handling with `error make`
- Type coercion with `into int`, `into string`, etc.

### Error Messages

**Python:**
```
Traceback (most recent call last):
File "bump-version.py", line 59
data = json.load(f)
json.decoder.JSONDecodeError: ...
```

**Nushell:**
```
Error: nu::shell::error
× Error: Invalid JSON in file
╭─[bump-version.nu:50:13]
50 │ let data = try {
· ─┬─
· ╰── originates from here
```

Nushell provides clearer, more actionable error messages with line numbers and context.

## Benefits of Nushell for This Project

1. **Native JSON Support**: No external libraries needed for JSON parsing
2. **Concise Syntax**: Less boilerplate code (155 lines in Python vs ~150 in Nushell)
3. **Better Error Handling**: More informative error messages
4. **Type Safety**: Fewer runtime errors
5. **Modern Shell**: Better integration with command-line workflows
6. **Performance**: Efficient structured data processing
7. **Maintainability**: Cleaner, more readable code

## Note on `inc` Command

Nushell's `inc` functionality for incrementing semantic version strings is now available as an official plugin: `nu_plugin_inc`.

**Our implementation uses a hybrid approach:**
1. First attempts to use `nu_plugin_inc` if installed
2. Falls back to custom implementation if plugin is not available

This provides the best of both worlds:
- Uses official plugin when available (recommended)
- Guarantees functionality even without the plugin
- No manual configuration needed

**To install the plugin:**
```bash
cargo install nu_plugin_inc
nu -c "plugin add ~/.cargo/bin/nu_plugin_inc"
```

See [NUSHELL_INC_COMMAND.md](./NUSHELL_INC_COMMAND.md) for complete details on plugin installation and usage.

## Installation

Nushell can be installed via:
- Snap: `sudo snap install nushell --classic`
- Cargo: `cargo install nu`
- Package managers: See https://www.nushell.sh/book/installation.html

## Compatibility

Both Python and Nushell versions are maintained:
- **Nushell** (`bump-version.nu`): Primary, recommended version
- **Python** (`bump-version.py`): Legacy version for compatibility

Both produce identical results and follow the same API.
Loading