%global debug_package %{nil}
%global __strip /bin/true
%global __provides_exclude_from ^/opt/OpenCode/.*$
%global __requires_exclude_from ^/opt/OpenCode/.*$
%global electron_mirror https://mirrors.huaweicloud.com/electron/
%global electron_builder_binaries_mirror https://mirrors.huaweicloud.com/electron-builder-binaries/
%global nodejs_version 24.18.0
%{!?opencode_cache_dir:%global opencode_cache_dir %{_builddir}/%{name}-%{version}/.cache/opencode-rpm}
# Both architecture variants of the vendored node_modules and of the Node.js
# toolchain are declared as separate Source entries with fixed file names, so a
# single SRPM carries the complete source set for aarch64 and x86_64. Deriving
# those file names from the build target architecture instead would resolve to
# a single variant per spec parse, leave the other one out of the SRPM and make
# that SRPM impossible to rebuild for the second architecture.
%ifarch aarch64
%global electron_builder_arch arm64
%global opencode_deps_source %{SOURCE3}
%global opencode_node_source %{SOURCE5}
%endif
%ifarch x86_64
%global electron_builder_arch x64
%global opencode_deps_source %{SOURCE2}
%global opencode_node_source %{SOURCE4}
%endif
%global opencode_build_target opencode-linux-%{electron_builder_arch}
%global electron_rpm_arch %{_target_cpu}
Name: opencode
Version: 1.17.13
Release: 2
Summary: The open source coding agent.
License: MIT
URL: https://github.com/anomalyco/opencode
%global source_archive v%{version}.tar.gz
Source0: https://github.com/anomalyco/%{name}/archive/refs/tags/%{source_archive}
# Build-time models.dev snapshot consumed by packages/opencode/script/build.ts.
Source1: opencode-models-api.json
# Vendored node_modules, one tarball per architecture.
Source2: opencode-deps-x86_64-%{version}.tar.xz
Source3: opencode-deps-aarch64-%{version}.tar.xz
# Pre-downloaded Node.js LTS, one tarball per architecture.
Source4: https://nodejs.org/dist/v%{nodejs_version}/node-v%{nodejs_version}-linux-x64.tar.xz
Source5: https://nodejs.org/dist/v%{nodejs_version}/node-v%{nodejs_version}-linux-arm64.tar.xz
# Desktop .desktop files and icons extracted from official upstream RPMs.
Source6: opencode-assets-%{version}.tar.gz
ExclusiveArch: aarch64 x86_64
BuildRequires: findutils
BuildRequires: git
BuildRequires: gzip
BuildRequires: cpio
BuildRequires: chrpath
BuildRequires: patch
BuildRequires: tar
BuildRequires: unzip
BuildRequires: which
BuildRequires: xz
BuildRequires: zip
Requires: git
%package desktop
Summary: Electron desktop app for OpenCode with bundled sidecar CLI
License: MIT
Requires: at-spi2-core
Requires: gtk3
Requires: libXScrnSaver
Requires: libnotify
Requires: nss
Requires: xdg-utils
Requires: (libXtst or libXtst6)
Requires: (libuuid or libuuid1)
%description
The open source AI coding agent
%description desktop
The open source AI coding agent
%prep
rm -rf %{name}-%{version}
mkdir -p %{name}-%{version}
tar -xzf %{_sourcedir}/%{source_archive} --strip-components=1 -C %{name}-%{version}
cd %{name}-%{version}
%build
cd %{name}-%{version}
export HOME="%{_builddir}/%{name}-%{version}/.home"
mkdir -p "$HOME"
# Use pre-downloaded Node.js LTS instead of system nodejs
mkdir -p "%{opencode_cache_dir}/nodejs"
tar -xJf "%{opencode_node_source}" --strip-components=1 -C "%{opencode_cache_dir}/nodejs"
export PATH="%{opencode_cache_dir}/nodejs/bin:$PATH"
export npm_config_registry="https://mirrors.huaweicloud.com/repository/npm/"
export NPM_CONFIG_REGISTRY="$npm_config_registry"
export npm_config_cache="%{opencode_cache_dir}/npm"
export NPM_CONFIG_CACHE="$npm_config_cache"
export NPM_CONFIG_PREFIX="%{opencode_cache_dir}/npm-global"
export PATH="$NPM_CONFIG_PREFIX/bin:$PATH"
export XDG_CACHE_HOME="%{opencode_cache_dir}/xdg"
mkdir -p "$NPM_CONFIG_PREFIX" "$npm_config_cache" "$XDG_CACHE_HOME"
export ELECTRON_MIRROR="%{electron_mirror}"
export ELECTRON_BUILDER_BINARIES_MIRROR="%{electron_builder_binaries_mirror}"
export ELECTRON_CACHE="%{opencode_cache_dir}/electron"
export ELECTRON_BUILDER_CACHE="%{opencode_cache_dir}/electron-builder"
mkdir -p "$ELECTRON_CACHE" "$ELECTRON_BUILDER_CACHE"
npm config set registry "$npm_config_registry"
npm config set cache "$npm_config_cache"
npm config set prefix "$NPM_CONFIG_PREFIX"
bun_version="$(node -p 'const manager = require("./package.json").packageManager || ""; if (!manager.startsWith("bun@")) { throw new Error("packageManager must be bun@<version>"); } manager.slice(4)')"
echo "Using bun version ${bun_version} from package.json"
if command -v bun >/dev/null 2>&1 && [ "$(bun --version)" = "$bun_version" ]; then
echo "Reusing cached bun $(bun --version)"
else
npm install -g "bun@${bun_version}"
fi
if command -v node-gyp >/dev/null 2>&1; then
echo "Reusing cached node-gyp $(node-gyp --version)"
else
npm install -g node-gyp
fi
bun --version
node-gyp --version
export BUN_INSTALL_CACHE_DIR="%{opencode_cache_dir}/bun/install/cache"
export BUN_RUNTIME_TRANSPILER_CACHE_PATH="%{opencode_cache_dir}/bun/runtime-cache"
export BUN_CONFIG_DISABLE_ANALYTICS=1
export MODELS_DEV_API_JSON="%{SOURCE1}"
export OPENCODE_VERSION="%{version}"
export OPENCODE_CHANNEL="prod"
export USE_HARD_LINKS=false
mkdir -p "$BUN_INSTALL_CACHE_DIR" "$(dirname "$BUN_RUNTIME_TRANSPILER_CACHE_PATH")"
# Extract pre-built node_modules then restore the three symlink
# categories bun only creates for root dependencies.
echo "Extracting pre-bundled node_modules"
tar -xJf "%{opencode_deps_source}" -C .
# 1. Package symlinks for transitive deps (flat + scoped)
echo "Restoring package symlinks"
ers=0
for link in node_modules/.bun/node_modules/* node_modules/.bun/node_modules/@*/*; do
[[ -L "$link" ]] || continue
pkg="${link#node_modules/.bun/node_modules/}"
if [[ ! -e "node_modules/$pkg" ]]; then
mkdir -p "$(dirname "node_modules/$pkg")"
case "$pkg" in
*/*) ln -sf "../.bun/node_modules/$pkg" "node_modules/$pkg" ;;
*) ln -sf "./.bun/node_modules/$pkg" "node_modules/$pkg" ;;
esac
ers=$((ers + 1))
fi
done
echo " $ers package symlinks"
# 2. .bin/ entries for transitive deps with bin scripts
echo "Restoring .bin/ symlinks"
bins=0
for bindir in node_modules/.bun/*/node_modules/.bin; do
[[ -d "$bindir" ]] || continue
for binfile in "$bindir"/*; do
[[ -L "$binfile" ]] || continue
name="$(basename "$binfile")"
[[ -e "node_modules/.bin/$name" ]] && continue
tgt="$(readlink "$binfile")"; pkg="${tgt#../}"; pkg="${pkg%%/*}"
[[ -e "node_modules/$pkg" ]] || continue
ln -sf "$tgt" "node_modules/.bin/$name"
bins=$((bins + 1))
done
done
echo " $bins .bin/ symlinks"
# 3. Workspace symlinks for non-root workspace deps
echo "Restoring workspace symlinks"
wss=0
for pkgjson in packages/*/package.json packages/*/*/package.json packages/*/*/*/package.json; do
[[ -f "$pkgjson" ]] || continue
name="$(node -p "require('./$pkgjson').name || ''" 2>/dev/null)"
[[ -n "$name" ]] || continue
[[ -e "node_modules/$name" ]] && continue
mkdir -p "node_modules/$(dirname "$name")"
ln -sf "../../$(dirname "$pkgjson")" "node_modules/$name"
wss=$((wss + 1))
done
echo " $wss workspace symlinks"
echo "Running postinstall (fix-node-pty)"
bun run --cwd packages/core fix-node-pty 2>/dev/null || :
pushd packages/opencode
bun run script/build.ts --single --skip-install
popd
cp -f "packages/opencode/dist/%{opencode_build_target}/bin/opencode" "packages/desktop/resources/opencode-cli"
pushd packages/desktop
bun run build
# Build Electron app without fpm RPM packaging.
# Desktop files and icons come from Source4 (official upstream RPMs).
cat > .electron-builder.override.yml <<'EOF'
npmRebuild: false
EOF
bun x electron-builder --linux dir --config electron-builder.config.ts --config .electron-builder.override.yml --publish never
desktop_share_cache=".desktop-share"
rm -rf "$desktop_share_cache"
mkdir -p "$desktop_share_cache"
# Extract only the current architecture's desktop files
tar -xzf "%{SOURCE6}" -C "$desktop_share_cache" "%{electron_rpm_arch}/usr"
mv "$desktop_share_cache"/%{electron_rpm_arch}/usr "$desktop_share_cache"/
rm -rf "$desktop_share_cache"/%{electron_rpm_arch}
find "$desktop_share_cache" -name '._*' -delete
popd
%install
cd %{name}-%{version}
install -d "%{buildroot}%{_bindir}"
install -Dm755 "packages/opencode/dist/%{opencode_build_target}/bin/opencode" "%{buildroot}%{_bindir}/opencode"
install -d "%{buildroot}%{_licensedir}/%{name}"
install -Dm644 LICENSE "%{buildroot}%{_licensedir}/%{name}/LICENSE"
install -d "%{buildroot}%{_licensedir}/%{name}-desktop"
install -Dm644 LICENSE "%{buildroot}%{_licensedir}/%{name}-desktop/LICENSE"
install -d "%{buildroot}%{_docdir}/%{name}"
install -Dm644 README.md "%{buildroot}%{_docdir}/%{name}/README.md"
desktop_unpack_dir=""
for candidate in packages/desktop/dist/linux-unpacked packages/desktop/dist/linux-%{electron_builder_arch}-unpacked; do
if [ -d "$candidate" ]; then
desktop_unpack_dir="$candidate"
break
fi
done
install -d "%{buildroot}/opt/OpenCode"
cp -a "$desktop_unpack_dir/." "%{buildroot}/opt/OpenCode/"
install -d "%{buildroot}%{_datadir}"
cp -a "packages/desktop/.desktop-share/usr/share/." "%{buildroot}%{_datadir}/"
desktop_app_root="%{buildroot}/opt/OpenCode"
desktop_launcher_name=""
for candidate in opencode OpenCode @opencode-aidesktop @opencode-aidesktop-electron; do
if [ -x "$desktop_app_root/$candidate" ]; then
desktop_launcher_name="$candidate"
break
fi
done
if [ -z "$desktop_launcher_name" ]; then
desktop_launcher_path="$(find "$desktop_app_root" -maxdepth 1 -type f -executable ! -name 'chrome-sandbox' ! -name 'chrome_crashpad_handler' ! -name '*.so*' | head -n 1)"
if [ -n "$desktop_launcher_path" ]; then
desktop_launcher_name="$(basename "$desktop_launcher_path")"
fi
fi
if [ -z "$desktop_launcher_name" ]; then
echo "Could not determine desktop launcher under $desktop_app_root" >&2
find "$desktop_app_root" -maxdepth 1 -type f >&2 || :
exit 1
fi
ln -s "../../opt/OpenCode/${desktop_launcher_name}" "%{buildroot}%{_bindir}/@opencode-aidesktop-electron"
ln -s "../../opt/OpenCode/${desktop_launcher_name}" "%{buildroot}%{_bindir}/opencode-desktop"
%check
cd %{name}-%{version}
./packages/opencode/dist/%{opencode_build_target}/bin/opencode --version | grep -F "%{version}"
desktop_unpack_dir=""
for candidate in ./packages/desktop/dist/linux-unpacked ./packages/desktop/dist/linux-%{electron_builder_arch}-unpacked; do
if [ -d "$candidate" ]; then
desktop_unpack_dir="$candidate"
break
fi
done
test -n "$desktop_unpack_dir"
desktop_launcher_path=""
for candidate in opencode OpenCode @opencode-aidesktop @opencode-aidesktop-electron; do
if [ -x "$desktop_unpack_dir/$candidate" ]; then
desktop_launcher_path="$desktop_unpack_dir/$candidate"
break
fi
done
if [ -z "$desktop_launcher_path" ]; then
desktop_launcher_path="$(find "$desktop_unpack_dir" -maxdepth 1 -type f -executable ! -name 'chrome-sandbox' ! -name 'chrome_crashpad_handler' ! -name '*.so*' | head -n 1)"
fi
test -n "$desktop_launcher_path"
test -x "$desktop_launcher_path"
%post -n %{name}-desktop
if command -v unshare >/dev/null 2>&1 && [[ -L /proc/self/ns/user ]] && unshare --user true >/dev/null 2>&1; then
chmod 0755 /opt/OpenCode/chrome-sandbox || :
else
chmod 4755 /opt/OpenCode/chrome-sandbox || :
fi
if command -v update-desktop-database >/dev/null 2>&1; then
update-desktop-database %{_datadir}/applications >/dev/null 2>&1 || :
fi
if command -v gtk-update-icon-cache >/dev/null 2>&1; then
gtk-update-icon-cache -q %{_datadir}/icons/hicolor >/dev/null 2>&1 || :
fi
%postun -n %{name}-desktop
if command -v update-desktop-database >/dev/null 2>&1; then
update-desktop-database %{_datadir}/applications >/dev/null 2>&1 || :
fi
if command -v gtk-update-icon-cache >/dev/null 2>&1; then
gtk-update-icon-cache -q %{_datadir}/icons/hicolor >/dev/null 2>&1 || :
fi
%files
%license %{_licensedir}/%{name}/LICENSE
%doc %{_docdir}/%{name}/README.md
%{_bindir}/opencode
%files desktop
%license %{_licensedir}/%{name}-desktop/LICENSE
%{_bindir}/@opencode-aidesktop-electron
%{_bindir}/opencode-desktop
/opt/OpenCode
%{_datadir}/applications/*.desktop
%{_datadir}/icons/hicolor/*/apps/*
%changelog
* Thu Sep 17 2026 hongyu-shi <shywzt@iCloud.com> - 1.17.13-2
- Ship the aarch64 and x86_64 variants of the vendored node_modules and Node.js
tarballs as separate Source entries with fixed names so the SRPM contains the
full dual-architecture source set, and select them at build time with %ifarch
* Thu Jul 02 2026 SIG-Intelligence <intelligence@openeuler.org> - 1.17.13-1
- Sync to upstream v1.17.13 release
* Wed Jul 01 2026 SIG-Intelligence <intelligence@openeuler.org> - 1.17.12-1
- Sync to upstream v1.17.12 release
* Mon Jun 15 2026 SIG-Intelligence <intelligence@openeuler.org> - 1.17.7-1
- Sync to upstream v1.17.7 release
* Tue Jun 02 2026 SIG-Intelligence <intelligence@openeuler.org> - 1.15.13-1
- Sync to upstream v1.15.13 release
* Fri May 29 2026 SIG-Intelligence <intelligence@openeuler.org> - 1.15.12-1
- Sync to upstream v1.15.12 release
* Fri May 29 2026 hongyu-shi <shywzt@iCloud.com> - 1.15.10-2
- Split opencode and opencode-desktop into a standalone spec
* Wed May 27 2026 SIG-Intelligence <intelligence@openeuler.org> - 1.15.10-1
- Sync to upstream v1.15.10 release
* Wed May 20 2026 SIG-Intelligence <intelligence@openeuler.org> - 1.15.5-1
- Sync to upstream v1.15.5 release
* Wed May 13 2026 SIG-Intelligence <intelligence@openeuler.org> - 1.14.48-1
- Sync to upstream v1.14.48 release
* Thu May 07 2026 SIG-Intelligence <intelligence@openeuler.org> - 1.14.40-1
- Sync to upstream v1.14.40 release
* Mon Apr 20 2026 SIG-Intelligence <intelligence@openeuler.org> - 1.14.18-1
- Sync to upstream v1.14.18 release
* Wed Apr 08 2026 SIG-Intelligence <intelligence@openeuler.org> - 1.4.0-1
- Sync to upstream v1.4.0 release
* Wed Apr 08 2026 hongyu-shi <shywzt@iCloud.com> - 1.3.17-2
- Split per-subpackage license metadata and ship the base package license text
* Tue Apr 07 2026 SIG-Intelligence <intelligence@openeuler.org> - 1.3.17-1
- Sync to upstream v1.3.17 release
* Thu Apr 02 2026 SIG-Intelligence <intelligence@openeuler.org> - 1.3.13-1
- Sync to upstream v1.3.13 release
* Mon Mar 30 2026 SIG-Intelligence <intelligence@openeuler.org> - 1.3.7-1
- Sync to upstream v1.3.7 release
* Wed Mar 25 2026 SIG-Intelligence <intelligence@openeuler.org> - 1.3.2-1
- Sync to upstream v1.3.2 release
* Wed Mar 18 2026 hongyu-shi <shywzt@iCloud.com> - 1.2.27-1
- Add openEuler RPM packaging for OpenCode CLI & Desktop
* Wed Jan 21 2026 Wangkui <wangkui35@h-partners.com> - 1.1.28-1
- add opencode