Skip to content
Closed
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
62 changes: 40 additions & 22 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ on:
push:
branches: [master, release-*]
tags:
- '[0-9]+.[0-9]+.[0-9]+'
- '[0-9]+.[0-9]+.[0-9]+-*'
- "[0-9]+.[0-9]+.[0-9]+"
- "[0-9]+.[0-9]+.[0-9]+-*"
pull_request:
workflow_dispatch:
jobs:
Expand All @@ -16,13 +16,13 @@ jobs:
strategy:
matrix:
include:
- os: windows-2019
- os: windows-2022
name: win-x86
param: -x86
- os: windows-2019
- os: windows-2022
name: win-x64
param: -x64
- os: windows-2019
- os: windows-2022
name: win-arm64
param: -arm64
- os: ubuntu-24.04
Expand All @@ -39,14 +39,14 @@ jobs:
name: linux-musl-arm
- os: ubuntu-24.04
name: linux-musl-arm64
- os: macos-13
- os: macos-26-intel
name: osx-x64
- os: macos-13
- os: macos-26
name: osx-arm64
fail-fast: false
steps:
- name: Checkout
uses: actions/checkout@v4.2.2
uses: actions/checkout@v6.0.2
with:
submodules: true
- name: Build Windows
Expand All @@ -56,39 +56,57 @@ jobs:
if: runner.os == 'macOS'
run: ./build.libgit2.sh
- name: Setup QEMU
if: matrix.name == 'linux-musl-arm' || matrix.name == 'linux-musl-arm64'
run: docker run --rm --privileged multiarch/qemu-user-static:register --reset
if: runner.os == 'Linux' && (matrix.name == 'linux-arm' || matrix.name == 'linux-arm64' || matrix.name == 'linux-ppc64le' || matrix.name == 'linux-musl-arm' || matrix.name == 'linux-musl-arm64')
uses: docker/setup-qemu-action@v4
- name: Set up Docker Buildx
if: runner.os == 'Linux'
uses: docker/setup-buildx-action@v4
- name: Build Linux
if: runner.os == 'Linux'
run: ./dockerbuild.sh
- name: Upload artifacts
uses: actions/upload-artifact@v4.4.3
uses: actions/upload-artifact@v7.0.0
with:
name: ${{ matrix.name }}
path: nuget.package/runtimes/${{ matrix.name }}
package:
name: Create package
needs: build
runs-on: ubuntu-24.04
env:
DOTNET_NOLOGO: true
steps:
name: Create package
needs: build
runs-on: ubuntu-24.04
env:
DOTNET_NOLOGO: true
steps:
- name: Checkout
uses: actions/checkout@v4.2.2
uses: actions/checkout@v6.0.2
with:
fetch-depth: 0
- name: Setup .NET SDK
uses: actions/setup-dotnet@v4.1.0
uses: actions/setup-dotnet@v5.2.0
with:
dotnet-version: 9.0.x
- name: Compute version suffix for branch builds
if: ${{ !startsWith(github.ref, 'refs/tags/') }}
id: version
run: |
# Sanitize branch name: lowercase, replace non-alphanumeric with hyphen, trim to 20 chars
BRANCH="${GITHUB_HEAD_REF:-${GITHUB_REF_NAME}}"
SAFE_BRANCH=$(echo "$BRANCH" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9]/-/g' | sed 's/--*/-/g' | sed 's/^-//' | sed 's/-$//' | cut -c1-20)
echo "override=${SAFE_BRANCH}.${{ github.run_number }}" >> "$GITHUB_OUTPUT"
- name: Download artifacts
uses: actions/download-artifact@v4.1.8
uses: actions/download-artifact@v8.0.1
with:
path: nuget.package/runtimes/
- name: Create package
run: dotnet pack nuget.package
run: dotnet pack nuget.package ${{ steps.version.outputs.override && format('/p:MinVerDefaultPreReleaseIdentifiers="{0}"', steps.version.outputs.override) || '' }}
- name: Upload NuGet package
uses: actions/upload-artifact@v4.4.3
uses: actions/upload-artifact@v7.0.0
with:
name: NuGet package
path: ./nuget.package/*.nupkg
- name: Push package to feed 🐙
id: push-feed
shell: bash
env:
FEED_API_KEY: ${{ secrets.FEED_API_KEY }}
FEED_SOURCE: ${{ secrets.FEED_SOURCE }}
run: dotnet nuget push ./nuget.package/*.nupkg --api-key "$FEED_API_KEY" --source "$FEED_SOURCE"
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,6 @@
bin/
obj/
.vs/
.claude/
libssh2-wincng-manifest/
libssh2-wincng-installed/
10 changes: 5 additions & 5 deletions Dockerfile.linux
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
FROM bording/crossbuild
ARG ARCH='amd64'
ENV CROSS_TRIPLE=${ARCH}
RUN apt update && apt -y install pkg-config
FROM debian:bookworm-slim
RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential cmake pkg-config libssl-dev libssh2-1-dev patchelf \
&& rm -rf /var/lib/apt/lists/*

WORKDIR /nativebinaries
COPY . /nativebinaries/

CMD ["/bin/bash", "-c", "./build.libgit2.sh"]
CMD ["/bin/bash", "-c", "./build.libgit2.sh"]
7 changes: 3 additions & 4 deletions Dockerfile.linux-musl
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
ARG ARCH='amd64'
FROM multiarch/alpine:${ARCH}-v3.13
RUN apk add --no-cache bash build-base cmake
FROM alpine:3.19
RUN apk add --no-cache bash build-base cmake openssl-dev libssh2-dev patchelf

WORKDIR /nativebinaries
COPY . /nativebinaries/

CMD ["/bin/bash", "-c", "./build.libgit2.sh"]
CMD ["/bin/bash", "-c", "./build.libgit2.sh"]
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,26 @@ nuget.exe Pack nuget.package/NativeBinaries.nuspec -Version <version> -NoPackage
Where `<version>` is the version from the MinVer tool or manually chosen version.


## Releasing

Releases are triggered by pushing a git tag. The tag format is:

```
<upstream-version>-octopus.<n>
```

Where `<upstream-version>` is the version from the upstream libgit2sharp.nativebinaries repo (e.g., `2.0.312`) and `<n>` is an incrementing number starting at 1. The incrementing number resets to 1 when the upstream version changes.

For example, for upstream version `2.0.312`:

```
git tag 2.0.312-octopus.1
git push origin 2.0.312-octopus.1
```

This triggers CI, which builds all native binaries, packs the NuGet package with the tag as its version, and pushes it to the configured feed.


## Notes on Visual Studio

Visual Studio 2019 is required to build the Windows native binaries, however you
Expand Down
4 changes: 2 additions & 2 deletions UpdateLibgit2ToSha.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ Push-Location $libgit2Directory
</Project>
"@

Set-Content -Encoding UTF8 (Join-Path $projectDirectory "nuget.package\build\LibGit2Sharp.NativeBinaries.props") $buildProperties
Set-Content -Encoding UTF8 (Join-Path $projectDirectory "nuget.package\build\Octopus.LibGit2Sharp.NativeBinaries.props") $buildProperties

$net46BuildProperties = @"
<Project>
Expand All @@ -116,7 +116,7 @@ Push-Location $libgit2Directory
</Project>
"@

Set-Content -Encoding UTF8 (Join-Path $projectDirectory "nuget.package\build\net46\LibGit2Sharp.NativeBinaries.props") $net46BuildProperties
Set-Content -Encoding UTF8 (Join-Path $projectDirectory "nuget.package\build\net46\Octopus.LibGit2Sharp.NativeBinaries.props") $net46BuildProperties

$dllConfig = @"
<configuration>
Expand Down
67 changes: 63 additions & 4 deletions build.libgit2.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ $x86Directory = Join-Path $projectDirectory "nuget.package\runtimes\win-x86\nati
$x64Directory = Join-Path $projectDirectory "nuget.package\runtimes\win-x64\native"
$arm64Directory = Join-Path $projectDirectory "nuget.package\runtimes\win-arm64\native"
$hashFile = Join-Path $projectDirectory "nuget.package\libgit2\libgit2_hash.txt"
$sha = Get-Content $hashFile
$sha = Get-Content $hashFile
$binaryFilename = "git2-" + $sha.Substring(0,7)

$build_tests = 'OFF'
Expand Down Expand Up @@ -101,6 +101,56 @@ function Assert-Consistent-Naming($expected, $path) {
Ensure-Property $expected $dll.VersionInfo.OriginalFilename "VersionInfo.OriginalFilename" $dll.Fullname
}

function Install-Libssh2($arch) {
$triplet = "$arch-windows"

$vcpkg = Join-Path $Env:VCPKG_INSTALLATION_ROOT "vcpkg.exe"
if (-not (Test-Path $vcpkg)) {
throw "Error: vcpkg not found at $Env:VCPKG_INSTALLATION_ROOT"
}

# Install libssh2 with the WinCNG crypto backend (no OpenSSL dependency).
# 'openssl' is a default vcpkg feature for libssh2, so manifest mode is required
# to override it — classic mode has no --no-default-features flag.
# The overlay triplet injects -DENABLE_ECDSA_WINCNG=ON into every package's
# cmake configure step (zlib ignores it; libssh2 uses it).
$manifestDir = Join-Path $projectDirectory "libssh2-wincng-manifest"
New-Item -ItemType Directory -Force -Path $manifestDir | Out-Null
@"
{
"name": "libssh2-wincng",
"version": "1.0.0",
"dependencies": [
{
"name": "libssh2",
"default-features": false,
"features": ["zlib"]
}
]
}
"@ | Set-Content (Join-Path $manifestDir "vcpkg.json")

$installRoot = Join-Path $projectDirectory "libssh2-wincng-installed"
$overlayTriplets = Join-Path $projectDirectory "libssh2-wincng-triplets"

Write-Host "Installing libssh2 (WinCNG + ECDSA) for $triplet via vcpkg..."
Push-Location $manifestDir
try {
Run-Command -Fatal -Quiet { & $vcpkg install --vcpkg-root $Env:VCPKG_INSTALLATION_ROOT --triplet $triplet "--x-install-root=$installRoot" "--overlay-triplets=$overlayTriplets" }
} finally {
Pop-Location
}

$installedDir = Join-Path $installRoot $triplet

return @{
IncludeDir = Join-Path $installedDir "include"
LibDir = Join-Path $installedDir "lib"
BinDir = Join-Path $installedDir "bin"
Prefix = $installedDir
}
}

try {
if ((!$x86.isPresent -and !$x64.IsPresent) -and !$arm64.IsPresent) {
Write-Output -Stderr "Error: usage $MyInvocation.MyCommand [-x86] [-x64] [-arm64]"
Expand All @@ -118,7 +168,8 @@ try {

if ($x86.IsPresent) {
Write-Output "Building x86..."
Run-Command -Fatal { & $cmake -A Win32 -D USE_SSH=exec -D USE_HTTPS=Schannel -D "BUILD_TESTS=$build_tests" -D "BUILD_CLI=OFF" -D "LIBGIT2_FILENAME=$binaryFilename" .. }
$ssh2 = Install-Libssh2 "x86"
Run-Command -Fatal { & $cmake -A Win32 -D USE_SSH=ON -D USE_HTTPS=Schannel -D "BUILD_TESTS=$build_tests" -D "BUILD_CLI=OFF" -D "LIBGIT2_FILENAME=$binaryFilename" -D "CMAKE_PREFIX_PATH=$($ssh2.Prefix)" .. }
Run-Command -Fatal { & $cmake --build . --config $configuration }
if ($test.IsPresent) { Run-Command -Quiet -Fatal { & $ctest -V . } }
cd $configuration
Expand All @@ -127,14 +178,17 @@ try {
Run-Command -Quiet { & rm $x86Directory\* -ErrorAction Ignore }
Run-Command -Quiet { & mkdir -fo $x86Directory }
Run-Command -Quiet -Fatal { & copy -fo * $x86Directory -Exclude *.lib }
Run-Command -Quiet -Fatal { & copy -fo (Join-Path $ssh2.BinDir "*.dll") $x86Directory }
if (-not (Test-Path (Join-Path $x86Directory "libssh2.dll"))) { throw "Error: libssh2.dll was not copied to $x86Directory" }
cd ..
}

if ($x64.IsPresent) {
Write-Output "Building x64..."
$ssh2 = Install-Libssh2 "x64"
Run-Command -Quiet { & mkdir build64 }
cd build64
Run-Command -Fatal { & $cmake -A x64 -D USE_SSH=exec -D USE_HTTPS=Schannel -D "BUILD_TESTS=$build_tests" -D "BUILD_CLI=OFF" -D "LIBGIT2_FILENAME=$binaryFilename" ../.. }
Run-Command -Fatal { & $cmake -A x64 -D USE_SSH=ON -D USE_HTTPS=Schannel -D "BUILD_TESTS=$build_tests" -D "BUILD_CLI=OFF" -D "LIBGIT2_FILENAME=$binaryFilename" -D "CMAKE_PREFIX_PATH=$($ssh2.Prefix)" ../.. }
Run-Command -Fatal { & $cmake --build . --config $configuration }
if ($test.IsPresent) { Run-Command -Quiet -Fatal { & $ctest -V . } }
cd $configuration
Expand All @@ -143,13 +197,16 @@ try {
Run-Command -Quiet { & rm $x64Directory\* -ErrorAction Ignore }
Run-Command -Quiet { & mkdir -fo $x64Directory }
Run-Command -Quiet -Fatal { & copy -fo * $x64Directory -Exclude *.lib }
Run-Command -Quiet -Fatal { & copy -fo (Join-Path $ssh2.BinDir "*.dll") $x64Directory }
if (-not (Test-Path (Join-Path $x64Directory "libssh2.dll"))) { throw "Error: libssh2.dll was not copied to $x64Directory" }
}

if ($arm64.IsPresent) {
Write-Output "Building arm64..."
$ssh2 = Install-Libssh2 "arm64"
Run-Command -Quiet { & mkdir buildarm64 }
cd buildarm64
Run-Command -Fatal { & $cmake -A ARM64 -D USE_SSH=exec -D USE_HTTPS=Schannel -D "BUILD_TESTS=$build_tests" -D "BUILD_CLI=OFF" -D "LIBGIT2_FILENAME=$binaryFilename" ../.. }
Run-Command -Fatal { & $cmake -A ARM64 -D USE_SSH=ON -D USE_HTTPS=Schannel -D "BUILD_TESTS=$build_tests" -D "BUILD_CLI=OFF" -D "LIBGIT2_FILENAME=$binaryFilename" -D "CMAKE_PREFIX_PATH=$($ssh2.Prefix)" ../.. }
Run-Command -Fatal { & $cmake --build . --config $configuration }
if ($test.IsPresent) { Run-Command -Quiet -Fatal { & $ctest -V . } }
cd $configuration
Expand All @@ -158,6 +215,8 @@ try {
Run-Command -Quiet { & rm $arm64Directory\* -ErrorAction Ignore }
Run-Command -Quiet { & mkdir -fo $arm64Directory }
Run-Command -Quiet -Fatal { & copy -fo * $arm64Directory -Exclude *.lib }
Run-Command -Quiet -Fatal { & copy -fo (Join-Path $ssh2.BinDir "*.dll") $arm64Directory }
if (-not (Test-Path (Join-Path $arm64Directory "libssh2.dll"))) { throw "Error: libssh2.dll was not copied to $arm64Directory" }
}

Write-Output "Done!"
Expand Down
30 changes: 29 additions & 1 deletion build.libgit2.sh
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ ARCH=`uname -m`
PACKAGEPATH="nuget.package/runtimes"
OSXARCHITECTURE=$ARCH

EXTRA_CMAKE_FLAGS=""

if [[ $OS == "Darwin" ]]; then
USEHTTPS="ON"
if [[ $RID == "osx-arm64" ]]; then
Expand All @@ -28,11 +30,12 @@ export _BINPATH=`pwd`

cmake -DCMAKE_BUILD_TYPE:STRING=Release \
-DBUILD_TESTS:BOOL=OFF \
-DUSE_SSH=exec \
-DUSE_SSH=ON \
-DLIBGIT2_FILENAME=git2-$SHORTSHA \
-DCMAKE_OSX_ARCHITECTURES=$OSXARCHITECTURE \
-DUSE_HTTPS=$USEHTTPS \
-DUSE_BUNDLED_ZLIB=ON \
$EXTRA_CMAKE_FLAGS \
..
cmake --build .

Expand All @@ -53,3 +56,28 @@ rm -rf $PACKAGEPATH/$RID
mkdir -p $PACKAGEPATH/$RID/native

cp libgit2/build/libgit2-$SHORTSHA.$LIBEXT $PACKAGEPATH/$RID/native

# Bundle libssh2 shared library alongside libgit2
LIBGIT2_PATH="$PACKAGEPATH/$RID/native/libgit2-$SHORTSHA.$LIBEXT"

if [[ $OS == "Darwin" ]]; then
echo "macOS: libssh2 sourced from global installation"
else
# Linux: find libssh2 via ldd
LIBSSH2_PATH=$(ldd "$LIBGIT2_PATH" | grep libssh2 | awk '{print $3}')
if [[ -z "$LIBSSH2_PATH" ]]; then
echo "ERROR: libgit2 does not appear to link against libssh2"
exit 1
fi

LIBSSH2_BASENAME=$(basename "$LIBSSH2_PATH")

echo "Bundling $LIBSSH2_BASENAME from $LIBSSH2_PATH"
cp "$LIBSSH2_PATH" "$PACKAGEPATH/$RID/native/$LIBSSH2_BASENAME"

# Set RPATH so libgit2 finds libssh2 in the same directory at runtime
patchelf --set-rpath '$ORIGIN' "$LIBGIT2_PATH"
fi

echo "Contents of $PACKAGEPATH/$RID/native/:"
ls -la "$PACKAGEPATH/$RID/native/"
Loading