diff --git a/CHANGELOG.md b/CHANGELOG.md index 12d3a9dce..57d0616de 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,21 @@ # Change Log +## v0.28.0-alpha.35 [(2025-06-03)](https://github.com/nodegit/nodegit/releases/tag/v0.28.0-alpha.35) + +[Full Changelog](https://github.com/nodegit/nodegit/compare/v0.28.0-alpha.34...v0.28.0-alpha.35) + +#### Summary of Changes + - Bump libgit2 to 1.9.1 + - Bump OpenSSL to 3.0 + - Move OpenSSL Packaging to Github Actions + - Add arm64 build Support + +#### Merged PRs into NodeGit +- [Bump libgit2 to 1.9.1](https://github.com/nodegit/nodegit/pull/2025) +- [Bump OpenSSL to 3.0, Move OpenSSL package generation to Github Actions](https://github.com/nodegit/nodegit/pull/2026) +- [fix: correct macos arch labels](github.com/nodegit/nodegit/pull/2027) +- [Add Ability to compile for arm64](https://github.com/nodegit/nodegit/pull/2028) + ## v0.28.0-alpha.34 [(2025-06-03)](https://github.com/nodegit/nodegit/releases/tag/v0.28.0-alpha.34) [Full Changelog](https://github.com/nodegit/nodegit/compare/v0.28.0-alpha.33...v0.28.0-alpha.34) diff --git a/generate/input/descriptor.json b/generate/input/descriptor.json index 889b792e7..99837534d 100644 --- a/generate/input/descriptor.json +++ b/generate/input/descriptor.json @@ -1526,6 +1526,11 @@ } } }, + "email": { + "cDependencies": [ + "git2/sys/email.h" + ] + }, "email_create_options": { "hasConstructor": true }, diff --git a/generate/input/libgit2-supplement.json b/generate/input/libgit2-supplement.json index 09433e7fa..742216f03 100644 --- a/generate/input/libgit2-supplement.json +++ b/generate/input/libgit2-supplement.json @@ -294,6 +294,54 @@ "isErrorCode": true } }, + "git_email_create_from_diff": { + "file": "sys/email.h", + "type": "function", + "isAsync": true, + "group": "email", + "args": [ + { + "name": "out", + "type": "git_buf *" + }, + { + "name": "diff", + "type": "git_diff *" + }, + { + "name": "patch_idx", + "type": "size_t" + }, + { + "name": "patch_count", + "type": "size_t" + }, + { + "name": "commit_id", + "type": "const git_oid *" + }, + { + "name": "summary", + "type": "const char *" + }, + { + "name": "body", + "type": "const char *" + }, + { + "name": "author", + "type": "const git_signature *" + }, + { + "name": "opts", + "type": "git_email_create_options *" + } + ], + "return": { + "type": "int", + "isErrorCode": true + } + }, "git_diff_get_perfdata": { "file": "sys/diff.h", "args": [ @@ -2619,6 +2667,9 @@ "blame": [ "git_blame_file" ], + "email": [ + "git_email_create_from_diff" + ], "note": [ "git_note_author", "git_note_commit_create", diff --git a/generate/templates/templates/binding.gyp b/generate/templates/templates/binding.gyp index 61cad0cb5..b5e189c32 100644 --- a/generate/templates/templates/binding.gyp +++ b/generate/templates/templates/binding.gyp @@ -160,9 +160,8 @@ "<(electron_openssl_root)/include" ], "libraries": [ - # this order is significant on centos7 apparently... - "<(electron_openssl_root)/lib/libssl.a", - "<(electron_openssl_root)/lib/libcrypto.a" + "<(electron_openssl_root)/lib64/libssl.a", + "<(electron_openssl_root)/lib64/libcrypto.a" ] }], ["<(is_electron) == 1 and <(electron_openssl_static) != 1", { diff --git a/utils/acquireOpenSSL.mjs b/utils/acquireOpenSSL.mjs index 1d83f540b..044454026 100644 --- a/utils/acquireOpenSSL.mjs +++ b/utils/acquireOpenSSL.mjs @@ -1,4 +1,5 @@ import crypto from "crypto"; +import { spawn } from "child_process"; import execPromise from "./execPromise.js"; import got from "got"; import path from "path"; @@ -7,19 +8,40 @@ import tar from "tar-fs"; import zlib from "zlib"; import { createWriteStream, promises as fs } from "fs"; import { performance } from "perf_hooks"; -import { fileURLToPath } from 'url'; import { promisify } from "util"; const pipeline = promisify(stream.pipeline); import packageJson from '../package.json' with { type: "json" }; -const OPENSSL_VERSION = "1.1.1t"; +const OPENSSL_VERSION = "3.0.18"; const win32BatPath = path.join(import.meta.dirname, "build-openssl.bat"); const vendorPath = path.resolve(import.meta.dirname, "..", "vendor"); const opensslPatchPath = path.join(vendorPath, "patches", "openssl"); const extractPath = path.join(vendorPath, "openssl"); +const exists = (filePath) => fs.stat(filePath).then(() => true).catch(() => false); + +const convertArch = (archStr) => { + const convertedArch = { + 'ia32': 'x86', + 'x86': 'x86', + 'x64': 'x64', + 'arm64': 'arm64' + }[archStr]; + + if (!convertedArch) { + throw new Error('unsupported architecture'); + } + + return convertedArch; +} + +const hostArch = convertArch(process.arch); +const targetArch = process.env.npm_config_arch + ? convertArch(process.env.npm_config_arch) + : hostArch; + const pathsToIncludeForPackage = [ "include", "lib" ]; @@ -56,6 +78,8 @@ const makeHashVerifyOnFinal = (expected) => (digest) => { // currently this only needs to be done on linux const applyOpenSSLPatches = async (buildCwd, operatingSystem) => { try { + await fs.access(opensslPatchPath); + for (const patchFilename of await fs.readdir(opensslPatchPath)) { const patchTarget = patchFilename.split("-")[1]; if (patchFilename.split(".").pop() === "patch" && (patchTarget === operatingSystem || patchTarget === "all")) { @@ -66,6 +90,11 @@ const applyOpenSSLPatches = async (buildCwd, operatingSystem) => { } } } catch(e) { + if (e.code === "ENOENT") { + // no patches to apply + return; + } + console.log("Patch application failed: ", e); throw e; } @@ -76,8 +105,10 @@ const buildDarwin = async (buildCwd, macOsDeploymentTarget) => { throw new Error("Expected macOsDeploymentTarget to be specified"); } + const buildConfig = targetArch === "x64" ? "darwin64-x86_64-cc" : "darwin64-arm64-cc"; + const configureArgs = [ - process.arch === "x64" ? "darwin64-x86_64-cc" : "darwin64-arm64-cc", + buildConfig, // speed up ecdh on little-endian platforms with 128bit int support "enable-ec_nistp_64_gcc_128", // compile static libraries @@ -86,6 +117,8 @@ const buildDarwin = async (buildCwd, macOsDeploymentTarget) => { "no-ssl2", "no-ssl3", "no-comp", + // disable tty ui since it fails a bunch of tests on GHA runners and we're just gonna link anyways + "no-ui-console", // set install directory `--prefix="${extractPath}"`, `--openssldir="${extractPath}"`, @@ -99,7 +132,7 @@ const buildDarwin = async (buildCwd, macOsDeploymentTarget) => { await applyOpenSSLPatches(buildCwd, "darwin"); - // only build the libraries, not the tests/fuzzer or apps + // only build the libraries, not the fuzzer or apps await execPromise("make build_libs", { cwd: buildCwd }, { pipeOutput: true }); @@ -115,13 +148,15 @@ const buildDarwin = async (buildCwd, macOsDeploymentTarget) => { }; const buildLinux = async (buildCwd) => { + const buildConfig = targetArch === "x64" ? "linux-x86_64" : "linux-aarch64"; + const configureArgs = [ - "linux-x86_64", + buildConfig, // Electron(at least on centos7) imports the libcups library at runtime, which has a // dependency on the system libssl/libcrypto which causes symbol conflicts and segfaults. // To fix this we need to hide all the openssl symbols to prevent them from being overridden // by the runtime linker. - "-fvisibility=hidden", + // "-fvisibility=hidden", // compile static libraries "no-shared", // disable ssl2, ssl3, and compression @@ -138,7 +173,7 @@ const buildLinux = async (buildCwd) => { await applyOpenSSLPatches(buildCwd, "linux"); - // only build the libraries, not the tests/fuzzer or apps + // only build the libraries, not the fuzzer or apps await execPromise("make build_libs", { cwd: buildCwd }, { pipeOutput: true }); @@ -154,44 +189,108 @@ const buildLinux = async (buildCwd) => { }, { pipeOutput: true }); }; -const buildWin32 = async (buildCwd, vsBuildArch) => { - if (!vsBuildArch) { - throw new Error("Expected vsBuildArch to be specified"); - } +const buildWin32 = async (buildCwd) => { + let vcvarsallPath = undefined; - const programFilesPath = (process.arch === "x64" - ? process.env["ProgramFiles(x86)"] - : process.env.ProgramFiles) || "C:\\Program Files"; - const vcvarsallPath = process.env.npm_config_vcvarsall_path || `${ - programFilesPath - }\\Microsoft Visual Studio\\2017\\BuildTools\\VC\\Auxiliary\\Build\\vcvarsall.bat`; - try { - await fs.stat(vcvarsallPath); - } catch { - throw new Error(`vcvarsall.bat not found at ${vcvarsallPath}`); + if (process.env.npm_config_vcvarsall_path && await exists(process.env.npm_config_vcvarsall_path)) { + vcvarsallPath = process.env.npm_config_vcvarsall_path; + } else { + const potentialMsvsPaths = []; + + // GYP_MSVS_OVERRIDE_PATH is set by node-gyp so this should cover most cases + if (process.env.GYP_MSVS_OVERRIDE_PATH) { + potentialMsvsPaths.push(process.env.GYP_MSVS_OVERRIDE_PATH); + } + + const packageTypes = ["BuildTools", "Community", "Professional", "Enterprise"]; + const versions = ["2022", "2019"] + + const computePossiblePaths = (parentPath) => { + let possiblePaths = [] + for (const packageType of packageTypes) { + for (const version of versions) { + possiblePaths.push(path.join(parentPath, version, packageType)); + } + } + + return possiblePaths; + } + + if (process.env["ProgramFiles(x86)"]) { + const parentPath = path.join(process.env["ProgramFiles(x86)"], 'Microsoft Visual Studio'); + potentialMsvsPaths.push(...computePossiblePaths(parentPath)); + } + + if (process.env.ProgramFiles) { + const parentPath = path.join(process.env.ProgramFiles, 'Microsoft Visual Studio'); + potentialMsvsPaths.push(...computePossiblePaths(parentPath)); + } + + for (const potentialPath of potentialMsvsPaths) { + const wholePath = path.join(potentialPath, 'VC', 'Auxiliary', 'Build', 'vcvarsall.bat'); + console.log("checking", wholePath); + if (await exists(wholePath)) { + vcvarsallPath = wholePath; + break; + } + } + + if (!vcvarsallPath) { + throw new Error(`vcvarsall.bat not found`); + } } let vcTarget; - switch (vsBuildArch) { - case "x64": { + switch (targetArch) { + case "x64": vcTarget = "VC-WIN64A"; break; - } - case "x86": { + case "x86": vcTarget = "VC-WIN32"; break; - } - - default: { - throw new Error(`Unknown vsBuildArch: ${vsBuildArch}`); - } + + case "arm64": + vcTarget = "VC-WIN64-ARM"; + break; } - await execPromise(`"${win32BatPath}" "${vcvarsallPath}" ${vsBuildArch} ${vcTarget}`, { - cwd: buildCwd, - maxBuffer: 10 * 1024 * 1024 // we should really just use spawn - }, { pipeOutput: true }); + let vsBuildArch = hostArch === targetArch + ? hostArch + : `${hostArch}_${targetArch}`; + + console.log("Using vcvarsall.bat at: ", vcvarsallPath); + console.log("Using vsBuildArch: ", vsBuildArch); + console.log("Using vcTarget: ", vcTarget); + + await new Promise((resolve, reject) => { + const buildProcess = spawn(`"${win32BatPath}" "${vcvarsallPath}" ${vsBuildArch} ${vcTarget}`, { + cwd: buildCwd, + shell: process.platform === "win32", + env: { + ...process.env, + NODEGIT_SKIP_TESTS: targetArch !== hostArch ? "1" : undefined + } + }); + + buildProcess.stdout.on("data", function(data) { + console.info(data.toString().trim()); + }); + + buildProcess.stderr.on("data", function(data) { + console.error(data.toString().trim()); + }); + + buildProcess.on("close", function(code) { + if (!code) { + resolve(); + } else { + reject(code); + } + }); + }); + + }; const removeOpenSSLIfOudated = async (openSSLVersion) => { @@ -235,8 +334,7 @@ const makeOnStreamDownloadProgress = () => { const buildOpenSSLIfNecessary = async ({ macOsDeploymentTarget, - openSSLVersion, - vsBuildArch + openSSLVersion }) => { if (process.platform !== "darwin" && process.platform !== "win32" && process.platform !== "linux") { console.log(`Skipping OpenSSL build, not required on ${process.platform}`); @@ -259,7 +357,7 @@ const buildOpenSSLIfNecessary = async ({ const openSSLUrl = getOpenSSLSourceUrl(openSSLVersion); const openSSLSha256Url = getOpenSSLSourceSha256Url(openSSLVersion); - const openSSLSha256 = (await got(openSSLSha256Url)).body.trim(); + const openSSLSha256 = (await got(openSSLSha256Url)).body.trim().split(' ')[0]; const downloadStream = got.stream(openSSLUrl); downloadStream.on("downloadProgress", makeOnStreamDownloadProgress()); @@ -280,7 +378,7 @@ const buildOpenSSLIfNecessary = async ({ } else if (process.platform === "linux") { await buildLinux(buildCwd); } else if (process.platform === "win32") { - await buildWin32(buildCwd, vsBuildArch); + await buildWin32(buildCwd); } else { throw new Error(`Unknown platform: ${process.platform}`); } @@ -332,18 +430,17 @@ const downloadOpenSSLIfNecessary = async ({ console.log("Download finished."); } -const getOpenSSLPackageName = () => { - let arch = process.arch; - if (process.platform === "win32" && ( - process.arch === "ia32" || process.env.NODEGIT_VS_BUILD_ARCH === "x86" - )) { - arch = "x86"; - } - - return `openssl-${OPENSSL_VERSION}-${process.platform}-${arch}.tar.gz`; +export const getOpenSSLPackageName = () => { + return `openssl-${OPENSSL_VERSION}-${process.platform}-${targetArch}.tar.gz`; } -const getOpenSSLPackageUrl = () => `${packageJson.binary.host}${getOpenSSLPackageName()}`; +export const getOpenSSLPackagePath = () => path.join(import.meta.dirname, getOpenSSLPackageName()); + +const getOpenSSLPackageUrl = () => { + const hostUrl = new URL(packageJson.binary.host); + hostUrl.pathname = getOpenSSLPackageName(); + return hostUrl.toString(); +}; const buildPackage = async () => { let resolve, reject; @@ -366,10 +463,10 @@ const buildPackage = async () => { new HashVerify("sha256", (digest) => { resolve(digest); }), - createWriteStream(getOpenSSLPackageName()) + createWriteStream(getOpenSSLPackagePath()) ); const digest = await promise; - await fs.writeFile(`${getOpenSSLPackageName()}.sha256`, digest); + await fs.writeFile(`${getOpenSSLPackagePath()}.sha256`, digest); }; const acquireOpenSSL = async () => { @@ -392,24 +489,15 @@ const acquireOpenSSL = async () => { let macOsDeploymentTarget; if (process.platform === "darwin") { - macOsDeploymentTarget = process.argv[2]; + macOsDeploymentTarget = process.argv[2] ?? process.env.OPENSSL_MACOS_DEPLOYMENT_TARGET if (!macOsDeploymentTarget || !macOsDeploymentTarget.match(/\d+\.\d+/)) { throw new Error(`Invalid macOsDeploymentTarget: ${macOsDeploymentTarget}`); } } - let vsBuildArch; - if (process.platform === "win32") { - vsBuildArch = process.env.NODEGIT_VS_BUILD_ARCH || (process.arch === "x64" ? "x64" : "x86"); - if (!["x64", "x86"].includes(vsBuildArch)) { - throw new Error(`Invalid vsBuildArch: ${vsBuildArch}`); - } - } - await buildOpenSSLIfNecessary({ openSSLVersion: OPENSSL_VERSION, - macOsDeploymentTarget, - vsBuildArch + macOsDeploymentTarget }); if (process.env.NODEGIT_OPENSSL_BUILD_PACKAGE) { await buildPackage(); @@ -427,5 +515,5 @@ if (process.argv[1] === import.meta.filename) { catch(error) { console.error("Acquire OpenSSL failed: ", error); process.exit(1); - }; + } } diff --git a/utils/build-openssl.bat b/utils/build-openssl.bat index 6e146cf89..af8063d7c 100644 --- a/utils/build-openssl.bat +++ b/utils/build-openssl.bat @@ -1,9 +1,18 @@ +rem Build OpenSSL for Windows +rem %1 - path to vcvarsall.bat +rem %2 - architecture argument for vcvarsall.bat +rem %3 - OpenSSL Configure target + @call %1 %2 perl .\Configure %3 no-shared no-ssl2 no-ssl3 no-comp --prefix="%cd%\.." --openssldir="%cd%\.." || goto :error nmake || goto :error -nmake test || goto :error + +if "%NODEGIT_SKIP_TESTS%" NEQ "1" ( + nmake test || goto :error +) + nmake install || goto :error goto :EOF diff --git a/vendor/libgit2.gyp b/vendor/libgit2.gyp index a155fb67f..f141e4d2d 100644 --- a/vendor/libgit2.gyp +++ b/vendor/libgit2.gyp @@ -315,13 +315,22 @@ # Workaround of a strange bug: # TargetMachine + static_library + x64 = nothing. "conditions": [ - ["target_arch=='x64'", { - "VCLibrarianTool": { - "AdditionalOptions": [ - "/MACHINE:X64", - ], + [ + "target_arch=='x64'", { + "VCLibrarianTool": { + "AdditionalOptions": [ + "/MACHINE:X64", + ], + }, + }, + "target_arch=='arm64'", { + "VCLibrarianTool": { + "AdditionalOptions": [ + "/MACHINE:ARM64", + ], + }, }, - }, { + { "VCLibrarianTool": { "AdditionalOptions": [ "/MACHINE:x86", diff --git a/vendor/patches/openssl/001-linux-force_getentropy_dso_lookup.patch b/vendor/patches/openssl/001-linux-force_getentropy_dso_lookup.patch deleted file mode 100644 index 6802c7fa5..000000000 --- a/vendor/patches/openssl/001-linux-force_getentropy_dso_lookup.patch +++ /dev/null @@ -1,23 +0,0 @@ -openssl doesn't have any sort of guard around this section of code other than -checking if we're compiling an elf binary on gnu linux. the syscall wrapper -`getentropy` is only available on glibc >= 2.25 which is a problem if we want -to support platforms like centos7 which ships with glibc 2.17. Attempting to -load this code on centos7 causes a runtime "undefined symbol error since glibc -doesn't provide it. -luckily openssl provides a backup lookup method in form of a dlopen call but -theres no way to configure for it, hence this patch. -Note further that centos7 doesn't have this function or the syscall it wraps -so the symbol lookup will fail and it will fallback to reading from /dev/random. -hence this patch just fixes compilation. -author: JZA ---- crypto/rand/rand_unix.c -+++ crypto/rand/rand_unix.c -@@ -372,7 +372,7 @@ static ssize_t syscall_random(void *buf, size_t buflen) - * Note: Sometimes getentropy() can be provided but not implemented - * internally. So we need to check errno for ENOSYS - */ --# if defined(__GNUC__) && __GNUC__>=2 && defined(__ELF__) && !defined(__hpux) -+# if defined(__GNUC__) && __GNUC__>=2 && defined(__ELF__) && !defined(__hpux) && 0 - extern int getentropy(void *buffer, size_t length) __attribute__((weak)); - - if (getentropy != NULL) {