%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