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
75 changes: 8 additions & 67 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,69 +14,14 @@ You can also use the command `session-info` for more info on mounts for this pro

## Script Versioning

**CRITICAL RULE**: ALL VERSION NUMBERS MUST BE IDENTICAL ACROSS ALL FILES. No exceptions.
Don't edit version strings by hand. CI sets them.

### Version Format
- Format: `YYYY.MM.DD.N`. `N` is the Nth release of that day.
- `copilot_here.sh`, `copilot_here.ps1`, and `app/Infrastructure/BuildInfo.cs` all carry the literal placeholder `0.0.0-dev` in source. They stay that way.
- The `compute-version` job in `.github/workflows/publish.yml` picks today's UTC date and the daily iteration. `scripts/stamp-version.sh` then writes the real string into the shell scripts before tests and packaging. The .NET binary's version comes from `Directory.Build.props` (local default: `today.0`) or from `-p:CopilotHereVersion=...` in CI. `BuildInfo.BuildDate` reads back from the assembly's `AssemblyInformationalVersion` at runtime.
- There is no `VERSION` file and no `bump-version.sh`. If you're reaching for one of those, stop. Open a PR with the code change and let CI pick the next number on merge.

- **Primary version**: Use current date in format `YYYY.MM.DD` (e.g., `2025.12.02`)
- **Same-day updates**: If the version date already matches today's date, append `.1`, `.2`, `.3`, etc.
- Example: `2025.12.02` → `2025.12.02.1` → `2025.12.02.2`
- **CRITICAL**: Always increment the version when making changes - this triggers re-download for users

### Where to Update Versions (ALL MUST MATCH)

**EVERY TIME** you modify shell functions, CLI binary code, or any functionality, update ALL FOUR version locations to the SAME version:

1. **Bash/Zsh script**: `copilot_here.sh`

- Line 2: `# Version: YYYY.MM.DD`
- Line 8: `COPILOT_HERE_VERSION="YYYY.MM.DD"`

2. **PowerShell script**: `copilot_here.ps1`

- Line 2: `# Version: YYYY.MM.DD`
- Line 8: `$script:CopilotHereVersion = "YYYY.MM.DD"`

3. **Build properties**: `Directory.Build.props`

- Reads `CopilotHereVersion` from the `VERSION` file automatically (no manual edit needed)

4. **Build info**: `app/Infrastructure/BuildInfo.cs`

- Line 13: `public const string BuildDate = "YYYY.MM.DD";`

### Verification Checklist

Before committing, verify all 5 locations have the EXACT SAME version:

```bash
# Quick check - all should show the same version
cat VERSION
grep "Version: " copilot_here.sh
grep "Version: " copilot_here.ps1
grep "COPILOT_HERE_VERSION=" copilot_here.sh
grep "CopilotHereVersion =" copilot_here.ps1
grep "BuildDate = " app/Infrastructure/BuildInfo.cs
```

Use `scripts/bump-version.sh YYYY.MM.DD` to update all locations at once.

### When to Update Version

- Any modification to shell function code
- Adding new features or options
- Bug fixes in the scripts or CLI binary
- Changes to the CLI binary code
- **Any commit that affects functionality should increment the version**
- **When in doubt, increment the version**

### Script File Synchronization

**CRITICAL**: The standalone script files (`copilot_here.sh` and `copilot_here.ps1`) are the source of truth.

- The README.md uses `curl` commands to download these files directly from the repository.
- Ensure both scripts are kept in sync regarding functionality and version numbers.
- Both scripts MUST have identical version numbers at all times.
See `docs/versioning.md` for the full flow.

## Technology Stack

Expand Down Expand Up @@ -707,15 +652,13 @@ git commit --no-gpg-sign -m "feat: Add multi-image build pipeline" \
2. Review documentation in `/docs` for requirements
3. Ensure changes align with project goals
4. Consider impact on both native binary and shell wrappers
5. Check if version numbers need updating

### Making Changes

1. Make minimal, surgical changes - change only what's necessary
2. Follow existing code patterns and conventions
3. Update relevant documentation if making structural changes
4. Test changes locally before committing
5. Update version numbers if changing functionality (see Script Versioning section)

### After Making Changes

Expand Down Expand Up @@ -807,7 +750,6 @@ docker run --rm -it copilot_here:test copilot --version
- Test affected functionality
- Review file changes with `git diff`
- Ensure documentation is updated
- Check version numbers are updated if functionality changed

### Edge Cases to Consider

Expand Down Expand Up @@ -869,9 +811,8 @@ Each image variant gets multiple tags:
9. **Minor tasks update existing files** - Don't create duplicate task files
10. **Document major changes** - Create task files for significant work
11. **Test before committing** - Build binary and verify functionality
12. **Update versions** - Increment version numbers when changing functionality
13. **Stop containers before updating** - dev-build.sh will prompt to stop running containers
14. **Debug logging** - Use `COPILOT_HERE_DEBUG=1` to enable detailed logging
12. **Stop containers before updating** - dev-build.sh will prompt to stop running containers
13. **Debug logging** - Use `COPILOT_HERE_DEBUG=1` to enable detailed logging

### When to Update These Instructions

Expand Down
73 changes: 56 additions & 17 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ jobs:
# Test shell function scripts can be sourced and call the native binary
test-shell-functions:
name: Shell Functions (${{ matrix.os }})
needs: [test-cli]
needs: [test-cli, compute-version]
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
Expand All @@ -98,8 +98,17 @@ jobs:
with:
global-json-file: global.json

# Stamp the in-memory `0.0.0-dev` sentinel to the computed version so the
# update-check inside copilot_here.sh / .ps1 doesn't compare against a
# remote-newer release and prompt during the --version test.
- name: Stamp version into shell scripts
shell: bash
run: |
chmod +x scripts/stamp-version.sh
./scripts/stamp-version.sh "${{ needs.compute-version.outputs.version }}"

- name: Build CLI binary
run: dotnet publish app/CopilotHere.csproj -c Release -o publish/shell-test --nologo
run: dotnet publish app/CopilotHere.csproj -c Release -p:CopilotHereVersion=${{ needs.compute-version.outputs.version }} -o publish/shell-test --nologo

- name: Test Bash script syntax
if: runner.os != 'Windows'
Expand Down Expand Up @@ -565,26 +574,52 @@ jobs:
working-directory: tests/CopilotHere.IntegrationTests
run: dotnet run --configuration Release

# Compute version from VERSION file + run number as revision
# Compute version: today's UTC date + iteration-of-the-day.
#
# `Release` mode (main + push OR workflow_dispatch) → iteration =
# (count of cli-v$DATE.* releases) + 1, matching the schema in
# docs/versioning.md.
# `Dev-stamp` mode (everything else) → iteration = COUNT (the latest
# already-released number for today). PR / scheduled / branch builds
# need to match-or-beat the cli-latest version so the shell wrappers'
# startup update-check doesn't fire during test-shell-functions; if no
# release exists yet today, COUNT is 0 and we stamp `$DATE.0`.
compute-version:
name: Compute Version
runs-on: ubuntu-24.04
outputs:
version: ${{ steps.compute.outputs.version }}
short_sha: ${{ steps.compute.outputs.short_sha }}
steps:
- name: Checkout repository
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4

- name: Compute version with revision
- name: Compute version
id: compute
env:
GH_TOKEN: ${{ github.token }}
GH_REPO: ${{ github.repository }}
run: |
BASE_VERSION=$(cat VERSION | tr -d '[:space:]')
VERSION="${BASE_VERSION}.${{ github.run_number }}"
echo "version=$VERSION" >> $GITHUB_OUTPUT
DATE=$(date -u +%Y.%m.%d)
SHORT_SHA="${GITHUB_SHA:0:7}"
echo "short_sha=$SHORT_SHA" >> $GITHUB_OUTPUT
echo "Computed version: $VERSION (run_number=${{ github.run_number }})"

COUNT=$(gh release list --limit 200 --json tagName \
--jq "[.[] | select(.tagName | startswith(\"cli-v${DATE}.\"))] | length")

IS_RELEASE=false
if [[ "${{ github.ref }}" == "refs/heads/main" ]] && \
{ [[ "${{ github.event_name }}" == "push" ]] || \
[[ "${{ github.event_name }}" == "workflow_dispatch" ]]; }; then
IS_RELEASE=true
fi

if [[ "$IS_RELEASE" == "true" ]]; then
N=$((COUNT + 1))
VERSION="${DATE}.${N}"
echo "Release version: ${VERSION} (release #${N} for ${DATE})"
else
VERSION="${DATE}.${COUNT}"
echo "Dev-stamp version: ${VERSION} (event=${{ github.event_name }}, ref=${{ github.ref }}, existing releases today=${COUNT})"
fi
Comment thread
GordonBeeming marked this conversation as resolved.
echo "version=${VERSION}" >> $GITHUB_OUTPUT

# Publish .NET Tool to NuGet
publish-nuget:
Expand Down Expand Up @@ -741,6 +776,15 @@ jobs:
with:
path: artifacts

# Stamp the source-of-truth scripts before copying them into release/.
# Earlier this ran *after* the copy and silently left the released
# copilot_here.sh / .ps1 at whatever the sentinel value was, breaking
# users' auto-update check.
- name: Stamp version into shell scripts
run: |
chmod +x scripts/stamp-version.sh
./scripts/stamp-version.sh "${{ needs.compute-version.outputs.version }}"

- name: Prepare release assets
run: |
mkdir -p release
Expand All @@ -750,7 +794,7 @@ jobs:
else
binary="copilot_here"
fi

# Create archive
cd artifacts/copilot_here-$rid
if [[ "$rid" == win-* ]]; then
Expand All @@ -770,11 +814,6 @@ jobs:

ls -la release/

- name: Stamp version into release shell scripts
run: |
chmod +x scripts/stamp-version.sh
./scripts/stamp-version.sh "${{ needs.compute-version.outputs.version }}"

- name: Create versioned release
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2
with:
Expand Down
9 changes: 7 additions & 2 deletions Directory.Build.props
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
<Project>
<PropertyGroup>
<!-- Central version: read from VERSION file. Override via -p:CopilotHereVersion=X.Y.Z -->
<CopilotHereVersion Condition="'$(CopilotHereVersion)' == ''">$([System.IO.File]::ReadAllText('$(MSBuildThisFileDirectory)VERSION').Trim())</CopilotHereVersion>
<!--
Local builds default to today's date with iteration .0 so the binary still
reports a YYYY.MM.DD.N version (matches the format tests). CI overrides via
-p:CopilotHereVersion=YYYY.MM.DD.N where N is the Nth release of the day,
computed by the `compute-version` job in .github/workflows/publish.yml.
-->
<CopilotHereVersion Condition="'$(CopilotHereVersion)' == ''">$([System.DateTime]::UtcNow.ToString('yyyy.MM.dd')).0</CopilotHereVersion>
</PropertyGroup>
</Project>
1 change: 0 additions & 1 deletion VERSION

This file was deleted.

50 changes: 41 additions & 9 deletions app/Infrastructure/BuildInfo.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,46 @@
using System.Reflection;

namespace CopilotHere.Infrastructure;

/// <summary>
/// Provides build-time information for the application.
/// The BuildDate is stamped during CI/CD from the VERSION file via scripts/stamp-version.sh.
/// </summary>
public static class BuildInfo
{
/// <summary>
/// The build date in yyyy.MM.dd or yyyy.MM.dd.N format.
/// This is replaced during build via MSBuild property.
/// </summary>
public const string BuildDate = "2026.05.13";
public static string BuildDate { get; } = ComputeBuildDate();

private static string ComputeBuildDate()
{
var informational = typeof(BuildInfo).Assembly
.GetCustomAttribute<AssemblyInformationalVersionAttribute>()
?.InformationalVersion;

if (string.IsNullOrEmpty(informational) ||
informational.StartsWith("0.0.0-dev", StringComparison.Ordinal))
{
return "0.0.0-dev";
}

// Strip the git-sha suffix appended by CopilotHere.csproj
// (`<InformationalVersion>$(Version).$(GitSha)</InformationalVersion>`)
// so consumers see just the YYYY.MM.DD.N portion. Stop at the 5th
// segment, or a '+' (SemVer build metadata). Pre-release tags ('-dev'
// etc.) are handled by the sentinel check above so we don't accidentally
// truncate them mid-string.
var dots = 0;
for (var i = 0; i < informational.Length; i++)
{
var c = informational[i];
if (c == '+')
{
return informational[..i];
}
if (c == '.')
{
dots++;
if (dots == 4)
{
return informational[..i];
}
}
}
return informational;
}
}
4 changes: 2 additions & 2 deletions copilot_here.ps1
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# copilot_here PowerShell functions
# Version: 2026.05.13
# Version: 0.0.0-dev
# Repository: https://github.com/GordonBeeming/copilot_here

# Set console output encoding to UTF-8 for Unicode character support
Expand All @@ -23,7 +23,7 @@ $script:DefaultCopilotHereBin = Join-Path $script:DefaultCopilotHereBinDir $scri

$script:CopilotHereBin = if ($env:COPILOT_HERE_BIN) { $env:COPILOT_HERE_BIN } else { $script:DefaultCopilotHereBin }
$script:CopilotHereReleaseUrl = "https://github.com/GordonBeeming/copilot_here/releases/download/cli-latest"
$script:CopilotHereVersion = "2026.05.13"
$script:CopilotHereVersion = "0.0.0-dev"

# Debug logging function
function Write-CopilotDebug {
Expand Down
4 changes: 2 additions & 2 deletions copilot_here.sh
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
# copilot_here shell functions
# Version: 2026.05.13
# Version: 0.0.0-dev
# Repository: https://github.com/GordonBeeming/copilot_here

# Configuration
COPILOT_HERE_BIN="${COPILOT_HERE_BIN:-$HOME/.local/bin/copilot_here}"
COPILOT_HERE_RELEASE_URL="https://github.com/GordonBeeming/copilot_here/releases/download/cli-latest"
COPILOT_HERE_VERSION="2026.05.13"
COPILOT_HERE_VERSION="0.0.0-dev"

# Ensure user bin directory is on PATH (required for the native binary + shell integration checks)
if [ -d "$HOME/.local/bin" ]; then
Expand Down
Loading
Loading