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
154 changes: 135 additions & 19 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ on:
pull_request:
workflow_dispatch:
jobs:
build:
build-windows:
name: ${{ matrix.name }}
runs-on: ${{ matrix.os }}
env:
Expand All @@ -29,6 +29,64 @@ jobs:
name: win-arm64
rid: win-arm64
param: -arm64
fail-fast: false
steps:
- name: Checkout
uses: actions/checkout@v6.0.2
with:
submodules: true
- name: Build Windows
run: ./build.libgit2.ps1 ${{ matrix.param }}
- name: Verify build output
shell: bash
run: ./verify-build.sh "${{ matrix.rid }}" "${{ matrix.variant }}"
- name: Upload artifacts
uses: actions/upload-artifact@v7.0.0
with:
name: ${{ matrix.name }}
path: nuget.package/runtimes/${{ matrix.rid }}

build-mac:
name: ${{ matrix.name }}
runs-on: ${{ matrix.os }}
env:
RID: ${{ matrix.rid }}
VARIANT: ${{ matrix.variant }}
strategy:
matrix:
include:
- os: macos-26-intel
name: osx-x64
rid: osx-x64
- os: macos-26
name: osx-arm64
rid: osx-arm64
fail-fast: false
steps:
- name: Checkout
uses: actions/checkout@v6.0.2
with:
submodules: true
- name: Build macOS
run: ./build.libgit2.sh
- name: Verify build output
shell: bash
run: ./verify-build.sh "${{ matrix.rid }}" "${{ matrix.variant }}"
- name: Upload artifacts
uses: actions/upload-artifact@v7.0.0
with:
name: ${{ matrix.name }}
path: nuget.package/runtimes/${{ matrix.rid }}

build-linux:
name: ${{ matrix.name }}
runs-on: ${{ matrix.os }}
env:
RID: ${{ matrix.rid }}
VARIANT: ${{ matrix.variant }}
strategy:
matrix:
include:
- os: ubuntu-24.04
name: linux-x64
rid: linux-x64
Expand Down Expand Up @@ -64,32 +122,18 @@ jobs:
- os: ubuntu-24.04
name: linux-musl-arm64
rid: linux-musl-arm64
- os: macos-26-intel
name: osx-x64
rid: osx-x64
- os: macos-26
name: osx-arm64
rid: osx-arm64
fail-fast: false
steps:
- name: Checkout
uses: actions/checkout@v6.0.2
with:
submodules: true
- name: Build Windows
if: runner.os == 'Windows'
run: ./build.libgit2.ps1 ${{ matrix.param }}
- name: Build macOS
if: runner.os == 'macOS'
run: ./build.libgit2.sh
- name: Setup QEMU
if: runner.os == 'Linux' && (matrix.rid == 'linux-arm' || matrix.rid == 'linux-arm64' || matrix.rid == 'linux-ppc64le' || matrix.rid == 'linux-musl-arm' || matrix.rid == 'linux-musl-arm64')
if: matrix.rid == 'linux-arm' || matrix.rid == 'linux-arm64' || matrix.rid == 'linux-ppc64le' || matrix.rid == 'linux-musl-arm' || matrix.rid == '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: Verify build output
shell: bash
Expand All @@ -99,9 +143,81 @@ jobs:
with:
name: ${{ matrix.name }}
path: nuget.package/runtimes/${{ matrix.rid }}
smoke-windows:
name: smoke-win-x64
needs: build-windows
runs-on: windows-2022
steps:
- name: Checkout
uses: actions/checkout@v6.0.2
- name: Download win-x64 artifact
uses: actions/download-artifact@v8.0.1
with:
name: win-x64
path: smoke-test/windows
- name: Compute libgit2 DLL name
id: dll
shell: bash
run: |
SHA=$(cat nuget.package/libgit2/libgit2_hash.txt)
echo "name=git2-${SHA:0:7}.dll" >> "$GITHUB_OUTPUT"
- name: Build static-CRT loader
shell: cmd
run: |
for /f "usebackq tokens=*" %%i in (`"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe" -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath`) do call "%%i\VC\Auxiliary\Build\vcvars64.bat"
cl /MT /O2 smoke-test\windows\loader.c /Fe:smoke-test\windows\loader.exe
- name: Build bare-Windows smoke image
run: docker build -t libgit2-smoke -f smoke-test/windows/Dockerfile smoke-test/windows
- name: Load git2 on bare Windows (no VC++ redist)
run: docker run --rm libgit2-smoke C:\native\${{ steps.dll.outputs.name }}
smoke-linux:
name: smoke-${{ matrix.name }}
needs: build-linux
runs-on: ubuntu-24.04
strategy:
fail-fast: false
matrix:
include:
- name: linux-x64-openssl3
artifact: linux-x64
build_base: debian:bookworm
runtime_base: debian:bookworm-slim
runtime_pkgs: libssl3
suffix: ""
- name: linux-x64-openssl1.1
artifact: linux-x64-openssl1.1
build_base: debian:bullseye
runtime_base: debian:bullseye-slim
runtime_pkgs: libssl1.1
suffix: "-openssl1.1"
steps:
- name: Checkout
uses: actions/checkout@v6.0.2
- name: Download artifact
uses: actions/download-artifact@v8.0.1
with:
name: ${{ matrix.artifact }}
path: smoke-test/linux
- name: Compute libgit2 .so name
id: so
run: |
SHA=$(cat nuget.package/libgit2/libgit2_hash.txt)
echo "name=libgit2-${SHA:0:7}${{ matrix.suffix }}.so" >> "$GITHUB_OUTPUT"
- name: Build smoke image
run: |
docker build -t libgit2-smoke \
--build-arg BUILD_BASE=${{ matrix.build_base }} \
--build-arg RUNTIME_BASE=${{ matrix.runtime_base }} \
--build-arg RUNTIME_PKGS=${{ matrix.runtime_pkgs }} \
-f smoke-test/linux/Dockerfile smoke-test/linux
- name: Load libgit2 in slim runtime
run: docker run --rm -e LD_LIBRARY_PATH=/native libgit2-smoke /native/${{ steps.so.outputs.name }}
package:
name: Create package
needs: build
needs:
- build-windows
- build-mac
- build-linux
runs-on: ubuntu-24.04
env:
DOTNET_NOLOGO: true
Expand Down Expand Up @@ -165,12 +281,12 @@ jobs:
run: dotnet nuget push ./nuget.package/*.nupkg --api-key "$FEED_API_KEY" --source "$FEED_SOURCE"
ci:
name: ci
needs: [build, package]
needs: [package, smoke-windows, smoke-linux]
runs-on: ubuntu-24.04
steps:
- name: Check results
run: |
if [ "${{ needs.build.result }}" != "success" ] || [ "${{ needs.package.result }}" != "success" ]; then
if [ "${{ needs.package.result }}" != "success" ] || [ "${{ needs.smoke-windows.result }}" != "success" ] || [ "${{ needs.smoke-linux.result }}" != "success" ]; then
echo "One or more jobs failed"
exit 1
fi
42 changes: 33 additions & 9 deletions build.libgit2.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,20 @@ function Assert-Consistent-Naming($expected, $path) {
Ensure-Property $expected $dll.VersionInfo.OriginalFilename "VersionInfo.OriginalFilename" $dll.Fullname
}

# libssh2_userauth_publickey_frommemory is quite fragile and can easily be left out by a misconfigured build or
# a changed dependency. This assertion tries to verify that we have the GIT_SSH_LIBSSH2_MEMORY_CREDENTIALS
# feature enabled and blows up the build if not.
function Assert-MemoryCredentials {
$featuresFile = Get-ChildItem -Path . -Recurse -Filter git2_features.h -ErrorAction SilentlyContinue | Select-Object -First 1
if (-not $featuresFile) {
throw "Assert-MemoryCredentials: git2_features.h not found after configure"
}
if (-not (Select-String -Path $featuresFile.FullName -Pattern 'define GIT_SSH_LIBSSH2_MEMORY_CREDENTIALS' -Quiet)) {
throw "GIT_SSH_LIBSSH2_MEMORY_CREDENTIALS not defined in $($featuresFile.FullName) - in-memory SSH credentials were silently disabled (static transitive deps not visible to the check_library_exists probe)"
}
Write-Host "Verified GIT_SSH_LIBSSH2_MEMORY_CREDENTIALS is defined ($($featuresFile.FullName))"
}

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

Expand Down Expand Up @@ -166,10 +180,22 @@ try {
Run-Command -Quiet { & mkdir build }
cd build

$commonCmakeArgs = @(
'-D', 'USE_SSH=ON'
'-D', 'USE_HTTPS=Schannel'
'-D', "BUILD_TESTS=$build_tests"
'-D', 'BUILD_CLI=OFF'
'-D', "LIBGIT2_FILENAME=$binaryFilename"
'-D', 'CMAKE_SHARED_LINKER_FLAGS=bcrypt.lib crypt32.lib'
'-D', 'HAVE_LIBSSH2_MEMORY_CREDENTIALS=1'
Comment on lines +189 to +190

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These two properties are new

)

if ($x86.IsPresent) {
Write-Output "Building x86..."
$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)" .. }
$cmakeArgs = @('-A', 'Win32') + $commonCmakeArgs + @('-D', "CMAKE_PREFIX_PATH=$($ssh2.Prefix)", '..')
Run-Command -Fatal { & $cmake @cmakeArgs }
Assert-MemoryCredentials
Run-Command -Fatal { & $cmake --build . --config $configuration }
if ($test.IsPresent) { Run-Command -Quiet -Fatal { & $ctest -V . } }
cd $configuration
Expand All @@ -178,8 +204,6 @@ 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 ..
}

Expand All @@ -188,7 +212,9 @@ try {
$ssh2 = Install-Libssh2 "x64"
Run-Command -Quiet { & mkdir build64 }
cd build64
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)" ../.. }
$cmakeArgs = @('-A', 'x64') + $commonCmakeArgs + @('-D', "CMAKE_PREFIX_PATH=$($ssh2.Prefix)", '../..')
Run-Command -Fatal { & $cmake @cmakeArgs }
Assert-MemoryCredentials
Run-Command -Fatal { & $cmake --build . --config $configuration }
if ($test.IsPresent) { Run-Command -Quiet -Fatal { & $ctest -V . } }
cd $configuration
Expand All @@ -197,16 +223,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=ON -D USE_HTTPS=Schannel -D "BUILD_TESTS=$build_tests" -D "BUILD_CLI=OFF" -D "LIBGIT2_FILENAME=$binaryFilename" -D "CMAKE_PREFIX_PATH=$($ssh2.Prefix)" ../.. }
$cmakeArgs = @('-A', 'ARM64') + $commonCmakeArgs + @('-D', "CMAKE_PREFIX_PATH=$($ssh2.Prefix)", '../..')
Run-Command -Fatal { & $cmake @cmakeArgs }
Assert-MemoryCredentials
Run-Command -Fatal { & $cmake --build . --config $configuration }
if ($test.IsPresent) { Run-Command -Quiet -Fatal { & $ctest -V . } }
cd $configuration
Expand All @@ -215,8 +241,6 @@ 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
4 changes: 2 additions & 2 deletions libssh2-wincng-triplets/wincng-options.cmake
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
set(VCPKG_CRT_LINKAGE dynamic)
set(VCPKG_LIBRARY_LINKAGE dynamic)
set(VCPKG_CRT_LINKAGE static)
set(VCPKG_LIBRARY_LINKAGE static)
set(VCPKG_CMAKE_CONFIGURE_OPTIONS "-DENABLE_ECDSA_WINCNG=ON")
15 changes: 15 additions & 0 deletions smoke-test/linux/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Build the loader on a full base, then run it in a slim runtime base that
# represents an intended target (matching OpenSSL present, no dev tooling).
ARG BUILD_BASE=debian:bookworm
ARG RUNTIME_BASE=debian:bookworm-slim
FROM ${BUILD_BASE} AS build
RUN apt-get update && apt-get install -y --no-install-recommends build-essential && rm -rf /var/lib/apt/lists/*
COPY loader.c /loader.c
RUN gcc -O2 -o /loader /loader.c -ldl

FROM ${RUNTIME_BASE}
ARG RUNTIME_PKGS=""
RUN if [ -n "$RUNTIME_PKGS" ]; then apt-get update && apt-get install -y --no-install-recommends $RUNTIME_PKGS && rm -rf /var/lib/apt/lists/*; fi
COPY --from=build /loader /usr/local/bin/loader
COPY native/ /native/
ENTRYPOINT ["/usr/local/bin/loader"]
31 changes: 31 additions & 0 deletions smoke-test/linux/loader.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/* Minimal dlopen loader for the Linux libgit2 native binary smoke test. */
#include <dlfcn.h>
#include <stdio.h>

typedef int (*git_libgit2_init_fn)(void);

int main(int argc, char **argv) {
if (argc < 2) {
fprintf(stderr, "usage: loader <path-to-libgit2.so>\n");
return 2;
}

const char *libraryPath = argv[1];

void *library = dlopen(libraryPath, RTLD_NOW | RTLD_GLOBAL);
if (!library) {
fprintf(stderr, "dlopen('%s') failed: %s\n", libraryPath, dlerror());
return 1;
}

git_libgit2_init_fn git_libgit2_init = (git_libgit2_init_fn)dlsym(library, "git_libgit2_init");
if (!git_libgit2_init) {
fprintf(stderr, "dlsym(git_libgit2_init) failed: %s\n", dlerror());
return 1;
}

int initCount = git_libgit2_init();
printf("git_libgit2_init() returned %d\n", initCount);

return initCount > 0 ? 0 : 1;
}
4 changes: 4 additions & 0 deletions smoke-test/windows/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
FROM mcr.microsoft.com/windows/nanoserver:ltsc2022
COPY native/ C:/native/
COPY loader.exe C:/loader.exe
ENTRYPOINT ["C:\\loader.exe"]
36 changes: 36 additions & 0 deletions smoke-test/windows/loader.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/* LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR makes the loader resolve git2's sibling DLLs
* from git2's own directory (where libssh2.dll/z.dll live in the broken build),
* so a missing transitive dep (VCRUNTIME140.dll) surfaces as ERROR_MOD_NOT_FOUND.
*/
#include <windows.h>
#include <stdio.h>

typedef int (*git_libgit2_init_fn)(void);

int main(int argc, char **argv) {
if (argc < 2) {
fprintf(stderr, "usage: loader <full-path-to-git2.dll>\n");
return 2;
}

const char *dllPath = argv[1];

HMODULE library = LoadLibraryExA(dllPath, NULL, LOAD_LIBRARY_SEARCH_DEFAULT_DIRS | LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR);
if (!library) {
DWORD error = GetLastError();
fprintf(stderr, "LoadLibraryEx('%s') failed: error %lu (0x%08lX)\n", dllPath, error, error);
return 1;
}

git_libgit2_init_fn git_libgit2_init = (git_libgit2_init_fn)GetProcAddress(library, "git_libgit2_init");
if (!git_libgit2_init) {
fprintf(stderr, "GetProcAddress(git_libgit2_init) failed: %lu\n", GetLastError());
return 1;
}

int initCount = git_libgit2_init();
printf("git_libgit2_init() returned %d\n", initCount);

/* git_libgit2_init returns the initialization count (>=1) on success. */
return initCount > 0 ? 0 : 1;
}
Loading
Loading