diff --git a/package.json b/package.json index 57dcd00..99ca18b 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,14 @@ { "from": "resources/slam-ros-bridge", "to": "slam-ros-bridge" + }, + { + "from": "resources/bin", + "to": "bin" + }, + { + "from": "resources/licenses", + "to": "licenses" } ], "linux": { diff --git a/resources/bin/linux-arm64/zstd b/resources/bin/linux-arm64/zstd new file mode 100755 index 0000000..051386f Binary files /dev/null and b/resources/bin/linux-arm64/zstd differ diff --git a/resources/bin/linux-x64/zstd b/resources/bin/linux-x64/zstd new file mode 100755 index 0000000..1d9f895 Binary files /dev/null and b/resources/bin/linux-x64/zstd differ diff --git a/resources/licenses/zstd/LICENSE b/resources/licenses/zstd/LICENSE new file mode 100644 index 0000000..7580028 --- /dev/null +++ b/resources/licenses/zstd/LICENSE @@ -0,0 +1,30 @@ +BSD License + +For Zstandard software + +Copyright (c) Meta Platforms, Inc. and affiliates. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + * Neither the name Facebook, nor Meta, nor the names of its contributors may + be used to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/src/main/index.ts b/src/main/index.ts index b939ae8..e19bc7f 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -71,6 +71,24 @@ function getAppIconPath(): string | undefined { return candidates.find((candidate) => existsSync(candidate)) } +function getBundledResourcePath(...segments: string[]): string | null { + const candidates = [ + join(process.resourcesPath, ...segments), + join(process.resourcesPath, 'resources', ...segments), + join(process.cwd(), 'resources', ...segments), + join(app.getAppPath(), 'resources', ...segments) + ] + + return candidates.find((candidate) => existsSync(candidate)) ?? null +} + +function getBundledZstdPath(): string | null { + if (process.platform !== 'linux') return null + const arch = process.arch === 'arm64' ? 'arm64' : process.arch === 'x64' ? 'x64' : null + if (!arch) return null + return getBundledResourcePath('bin', `linux-${arch}`, 'zstd') +} + type CachedModuleFiles = { root: string | null absoluteRoot: string | null @@ -610,7 +628,11 @@ async function ensureArtifactFile(url?: string | null, fileName?: string | null, function tarArgsFor(filePath: string, mode: 'list' | 'extract'): string[] { const lower = filePath.toLowerCase() - if (lower.endsWith('.tar.zst') || lower.endsWith('.tzst')) return mode === 'list' ? ['--zstd', '-tf'] : ['--zstd', '-xf'] + if (lower.endsWith('.tar.zst') || lower.endsWith('.tzst')) { + const zstdPath = getBundledZstdPath() + const compressionArgs = zstdPath ? [`--use-compress-program=${zstdPath}`] : ['--zstd'] + return mode === 'list' ? [...compressionArgs, '-tf'] : [...compressionArgs, '-xf'] + } if (lower.endsWith('.tar.gz') || lower.endsWith('.tgz')) return mode === 'list' ? ['-tzf'] : ['-xzf'] return mode === 'list' ? ['-tf'] : ['-xf'] } @@ -659,7 +681,8 @@ async function prepareRuntimeImages(images?: RuntimeImageArtifact[] | null): Pro continue } if (filePath.toLowerCase().endsWith('.tar.zst') || filePath.toLowerCase().endsWith('.tzst')) { - await runCommand('bash', ['-lc', 'zstd -dc "$1" | docker load', 'autonomy-studio-runtime-image', filePath], undefined, undefined, 900000) + const zstdPath = getBundledZstdPath() ?? 'zstd' + await runCommand('bash', ['-lc', '"$1" -dc "$2" | docker load', 'autonomy-studio-runtime-image', zstdPath, filePath], undefined, undefined, 900000) } else { await runCommand('docker', ['load', '-i', filePath], undefined, undefined, 900000) }