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
1 change: 1 addition & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* @joshbw
42 changes: 42 additions & 0 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Copilot Instructions for Rando-utilities

## Repository overview

A collection of cross-platform shell utilities and external tool submodules. No build system, test framework, or package manager — these are standalone scripts.

## Repository structure

- `src/helpful_scripts/` — Original shell utilities, each implemented as a .cmd/.ps1/.sh triplet (except `RefreshEnv.cmd` and `setup_machine.ps1` which are Windows-only)
- `src/external/` — Git submodules pulling in other utility repos (e.g., `rscalc`). These have their own build systems and copilot instructions — defer to those.

## Conventions

### Cross-platform triplets

`math`, `up`, and `mdcd` each exist as `.cmd`, `.ps1`, and `.sh` files implementing identical behavior per platform. When modifying logic in one variant, update all three to stay in sync.

### Script documentation

- PowerShell scripts use full comment-based help blocks (`.SYNOPSIS`, `.DESCRIPTION`, `.PARAMETER`, `.EXAMPLE`)
- Bash scripts use inline comments
- CMD scripts have minimal or no comments

### Naming

Lowercase filenames, no underscores in utility names (except `setup_machine.ps1` which is a provisioning script, not a daily utility).

## Building

Run `./build.ps1` (requires PowerShell and `cargo`) to build all Rust projects in `src/external/` for the current platform and assemble output into `.dist/`. Use `-Clean` to wipe `.dist/` first.

## CI/CD

`release.yml` runs on push/PR to `master`:
1. **Build job** (matrix: windows, linux, macos): checks out with submodules, installs Rust, builds all Rust projects in `src/external/`, collects binaries + scripts into `.dist/`, uploads as artifacts
2. **Release job** (push only, windows runner): downloads all platform artifacts, signs `.exe` and `.ps1` files via Azure Trusted Signing, copies signed `.ps1` files to other platform bundles, creates per-platform zip archives, publishes GitHub Release

When adding new Rust projects to `src/external/`, the build discovers them automatically via `Cargo.toml`. No workflow changes needed for new projects. New scripts in `src/helpful_scripts/` are also picked up automatically.

## Submodules

This repo uses git submodules under `src/external/`. Always clone with `--recurse-submodules` or run `git submodule update --init --recursive` after cloning. Submodules have their own copilot instructions — follow those when working inside them.
140 changes: 116 additions & 24 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
name: Release

on:
# Triggers the workflow on push or pull request events but only for the "master" branch
push:
branches: [ "master" ]
pull_request:
Expand All @@ -11,13 +10,116 @@ permissions:
contents: write

jobs:
release:
runs-on: windows-latest
build:
strategy:
matrix:
include:
- os: windows-latest
platform: windows
binary_ext: .exe
- os: ubuntu-latest
platform: linux
binary_ext: ""
- os: macos-latest
platform: macos
binary_ext: ""
runs-on: ${{ matrix.os }}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
submodules: recursive

- name: Install Rust
uses: dtolnay/rust-toolchain@stable

- name: Build external projects
shell: pwsh
run: |
Get-ChildItem -Path src/external -Directory | ForEach-Object {
$cargoToml = Join-Path $_.FullName 'Cargo.toml'
if (Test-Path $cargoToml) {
Write-Host "Building $($_.Name)..."
Push-Location $_.FullName
cargo build --release
if ($LASTEXITCODE -ne 0) { throw "Build failed for $($_.Name)" }
Pop-Location
}
}

- name: Collect artifacts
shell: pwsh
run: |
$distDir = '.dist'
New-Item -ItemType Directory -Force -Path $distDir | Out-Null

Get-ChildItem -Path src/external -Directory | ForEach-Object {
$cargoToml = Join-Path $_.FullName 'Cargo.toml'
if (Test-Path $cargoToml) {
$name = (Get-Content $cargoToml |
Select-String '^name\s*=\s*"(.+)"' |
Select-Object -First 1).Matches.Groups[1].Value
$ext = '${{ matrix.binary_ext }}'
$binary = Join-Path $_.FullName "target/release/${name}${ext}"
if (Test-Path $binary) {
Copy-Item $binary -Destination $distDir
} else {
throw "Expected binary not found: $binary"
}
}
}

- name: Sign PowerShell scripts
Copy-Item -Path src/helpful_scripts/* -Destination $distDir
Copy-Item LICENSE, README.md -Destination $distDir

- name: Set executable permissions
if: matrix.platform != 'windows'
run: |
chmod +x .dist/*.sh
find .dist -maxdepth 1 -type f ! -name '*.*' -exec chmod +x {} +

- name: Create platform archive (Linux/macOS)
if: matrix.platform != 'windows'
run: tar czf jbw_utils-${{ matrix.platform }}.tar.gz -C .dist .

- name: Upload archive (Linux/macOS)
if: matrix.platform != 'windows'
uses: actions/upload-artifact@v4
with:
name: archive-${{ matrix.platform }}
path: jbw_utils-${{ matrix.platform }}.tar.gz

- name: Upload artifacts (Windows)
if: matrix.platform == 'windows'
uses: actions/upload-artifact@v4
with:
name: dist-windows
path: .dist/

release:
needs: build
if: github.event_name == 'push'
runs-on: windows-latest
steps:
- name: Download Windows artifacts
uses: actions/download-artifact@v4
with:
name: dist-windows
path: dist-windows

- name: Download Linux archive
uses: actions/download-artifact@v4
with:
name: archive-linux
path: archives

- name: Download macOS archive
uses: actions/download-artifact@v4
with:
name: archive-macos
path: archives

- name: Sign Windows executables and PowerShell scripts
uses: azure/trusted-signing-action@v0
with:
azure-tenant-id: ${{ secrets.AZURE_TENANT_ID }}
Expand All @@ -26,39 +128,29 @@ jobs:
endpoint: ${{ secrets.TRUSTED_SIGNING_ENDPOINT }}
trusted-signing-account-name: ${{ secrets.TRUSTED_SIGNING_ACCOUNT_NAME }}
certificate-profile-name: ${{ secrets.TRUSTED_SIGNING_CERT_PROFILE }}
files-folder: ${{ github.workspace }}
files-folder-filter: ps1
files-folder: dist-windows
files-folder-filter: exe,ps1
file-digest: SHA256
timestamp-rfc3161: http://timestamp.acs.microsoft.com
timestamp-digest: SHA256

- name: Generate release tag
id: tag
shell: pwsh
run: |
$tag = "v$(Get-Date -Format 'yyyy.MM.dd')-$("${{ github.sha }}".Substring(0,7))"
echo "RELEASE_TAG=$tag" >> $env:GITHUB_OUTPUT

- name: Zip utilities
run: |
Compress-Archive -Path `
LICENSE, `
README.md, `
RefreshEnv.cmd, `
math.cmd, `
math.ps1, `
math.sh, `
mdcd.cmd, `
mdcd.ps1, `
mdcd.sh, `
setup_machine.ps1, `
up.cmd, `
up.ps1, `
up.sh `
-DestinationPath jbw_utils.zip
- name: Create Windows archive
shell: pwsh
run: Compress-Archive -Path dist-windows/* -DestinationPath jbw_utils-windows.zip

- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ steps.tag.outputs.RELEASE_TAG }}
files: jbw_utils.zip
files: |
jbw_utils-windows.zip
archives/jbw_utils-linux.tar.gz
archives/jbw_utils-macos.tar.gz
generate_release_notes: true
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/.dist/
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "src/external/rscalc"]
path = src/external/rscalc
url = https://github.com/joshbw/rscalc.git
75 changes: 75 additions & 0 deletions build.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
<#
.SYNOPSIS
Builds all projects and assembles output into .dist/

.DESCRIPTION
Builds all Rust projects in src/external/ for the current platform,
then copies release binaries and helpful scripts into .dist/.

.PARAMETER Clean
Remove .dist/ before building.

.EXAMPLE
./build.ps1
./build.ps1 -Clean
#>
param(
[switch]$Clean
)

$ErrorActionPreference = 'Stop'

$repoRoot = $PSScriptRoot
$distDir = Join-Path $repoRoot '.dist'

if ($Clean -and (Test-Path $distDir)) {
Remove-Item -Recurse -Force $distDir
}

New-Item -ItemType Directory -Force -Path $distDir | Out-Null

# Determine binary extension for current platform
if ($IsWindows -or $env:OS -eq 'Windows_NT') {
$binaryExt = '.exe'
} else {
$binaryExt = ''
}

# Build all Rust projects in src/external/
$externalDir = Join-Path $repoRoot 'src' 'external'
Get-ChildItem -Path $externalDir -Directory | ForEach-Object {
$cargoToml = Join-Path $_.FullName 'Cargo.toml'
if (Test-Path $cargoToml) {
Write-Host "Building $($_.Name)..." -ForegroundColor Cyan
Push-Location $_.FullName
try {
cargo build --release
if ($LASTEXITCODE -ne 0) { throw "cargo build failed for $($_.Name)" }

$packageName = (Get-Content $cargoToml |
Select-String '^name\s*=\s*"(.+)"' |
Select-Object -First 1).Matches.Groups[1].Value
$binary = Join-Path $_.FullName "target" "release" "$packageName$binaryExt"

if (Test-Path $binary) {
Copy-Item $binary -Destination $distDir
Write-Host " Copied $packageName$binaryExt to .dist/" -ForegroundColor Green
} else {
throw "Expected binary not found: $binary"
}
} finally {
Pop-Location
}
}
}

# Copy helpful scripts
$scriptsDir = Join-Path $repoRoot 'src' 'helpful_scripts'
Copy-Item -Path (Join-Path $scriptsDir '*') -Destination $distDir
Write-Host "Copied helpful scripts to .dist/" -ForegroundColor Green

# Copy repo docs
Copy-Item -Path (Join-Path $repoRoot 'LICENSE') -Destination $distDir
Copy-Item -Path (Join-Path $repoRoot 'README.md') -Destination $distDir

Write-Host "`nBuild complete. Output in .dist/" -ForegroundColor Cyan
1 change: 1 addition & 0 deletions src/external/rscalc
Submodule rscalc added at 396f79
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Loading