Skip to content

Commit 94d868f

Browse files
committed
[1.1.0] Fix KeyError and add platform-aware downloads
1 parent 64d562d commit 94d868f

5 files changed

Lines changed: 100 additions & 30 deletions

File tree

jb_updater/spec/utils_spec.cr

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,27 @@ describe Utils do
2727
Utils.build_in_range?("2023.1.0", "2024.0.0", "2024.3.0").should be_false
2828
end
2929
end
30+
31+
describe ".product_code" do
32+
it "maps known product names" do
33+
Utils.product_code("RubyMine2025.2").should eq "RM"
34+
Utils.product_code("WebStorm2025.1").should eq "WS"
35+
Utils.product_code("PyCharm2024.3").should eq "PY"
36+
Utils.product_code("CLion2025.1").should eq "CL"
37+
Utils.product_code("GoLand2025.2").should eq "GO"
38+
Utils.product_code("IntelliJ IDEA 2025.2").should eq "IU"
39+
Utils.product_code("PhpStorm2025.1").should eq "PS"
40+
Utils.product_code("Rider2025.1").should eq "RD"
41+
end
42+
43+
it "matches lowercase names (e.g. from --ide-path)" do
44+
Utils.product_code("phpstorm").should eq "PS"
45+
Utils.product_code("webstorm").should eq "WS"
46+
Utils.product_code("intellij").should eq "IU"
47+
end
48+
49+
it "falls back to first two uppercase characters for unknown names" do
50+
Utils.product_code("MyCustomIDE").should eq "MY"
51+
end
52+
end
3053
end

jb_updater/src/jb_updater/detect_products.cr

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -153,19 +153,7 @@ module JBUpdater
153153
# @param name [String] Product name (e.g. "RubyMine2025.2")
154154
# @return [String] Product code (e.g. "RM")
155155
def infer_code(name : String) : String
156-
mapping = {
157-
"RubyMine" => "RM",
158-
"WebStorm" => "WS",
159-
"PyCharm" => "PY",
160-
"CLion" => "CL",
161-
"GoLand" => "GO",
162-
"IntelliJ" => "IU",
163-
"PhpStorm" => "PS",
164-
"Rider" => "RD",
165-
}
166-
167-
key = name.gsub(/[\d ].*/, "")
168-
mapping[key]? || name[0, 2].upcase
156+
Utils.product_code(name)
169157
end
170158

171159
# Converts a product name and code into an API build string.

jb_updater/src/jb_updater/ide_updater.cr

Lines changed: 50 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -68,21 +68,53 @@ module JBUpdater
6868
data = JSON.parse(body)
6969
version = data[product_code][0]["version"].as_s
7070
downloads = data[product_code][0]["downloads"]
71-
link = nil
7271

73-
arch = opts.arch || autodetect_arch
72+
platform_key = download_key_for(platform, opts.arch)
7473

75-
if arch == "arm" && downloads["macM1"]?
76-
link = downloads["macM1"]["link"].as_s
77-
else
78-
link = downloads["mac"]["link"].as_s
79-
end
74+
entry = downloads[platform_key]? || raise "No download entry found for platform '#{platform}' (#{platform_key}) for #{product_code}"
75+
link = entry["link"].as_s
8076

8177
dmg_url = link
8278
Log.success("Latest version #{version}")
8379
URI.parse(dmg_url)
8480
end
8581

82+
# Determines the current OS platform.
83+
#
84+
# @return [String] `"mac"`, `"linux"`, or `"windows"`
85+
private def platform : String
86+
{% if flag?(:darwin) %}
87+
"mac"
88+
{% elsif flag?(:linux) %}
89+
"linux"
90+
{% elsif flag?(:win32) %}
91+
"windows"
92+
{% else %}
93+
raise "Unsupported OS"
94+
{% end %}
95+
end
96+
97+
# Maps platform + architecture to the JetBrains releases API download key.
98+
#
99+
# - **mac** (Apple Silicon): `macM1`; otherwise: `mac`
100+
# - **linux** (ARM64): `linuxARM64`; otherwise: `linux`
101+
# - **windows**: `windows`
102+
#
103+
# @param platform [String] OS platform (`"mac"`, `"linux"`, `"windows"`)
104+
# @param arch [String?] Architecture (`"arm"` or `"intel"`); autodetected if nil
105+
# @return [String] Download key in the JetBrains releases API payload
106+
private def download_key_for(platform : String, arch : String?) : String
107+
cpu = arch || autodetect_arch
108+
case platform
109+
when "mac"
110+
cpu == "arm" ? "macM1" : "mac"
111+
when "linux"
112+
cpu == "arm" ? "linuxARM64" : "linux"
113+
else
114+
"windows"
115+
end
116+
end
117+
86118
# Detects the CPU architecture by running `uname -m`.
87119
#
88120
# @return [String] `"arm"` or `"intel"`
@@ -104,16 +136,12 @@ module JBUpdater
104136

105137
# Maps a product name to its JetBrains product code.
106138
#
107-
# @param product [String] Product name (e.g. `"RubyMine2025.2"`)
108-
# @return [String] Product code (e.g. `"RM"`)
139+
# Delegates to {JBUpdater.Utils.product_code} (case-insensitive).
140+
#
141+
# @param product [String] Product name (e.g. `"PhpStorm2025.1"` or `"phpstorm"`)
142+
# @return [String] Product code (e.g. `"PS"`)
109143
private def infer_product_code(product : String) : String
110-
{
111-
"RubyMine" => "RM",
112-
"WebStorm" => "WS",
113-
"PyCharm" => "PY",
114-
"CLion" => "CL",
115-
"GoLand" => "GO",
116-
}[product.gsub(/\d+.*/, "")] || product[0, 2].upcase
144+
Utils.product_code(product)
117145
end
118146

119147
# ------------------------------------------------------------------------
@@ -126,7 +154,12 @@ module JBUpdater
126154
# @param product [String] Product name (for logging)
127155
# @param uri [URI] Download URL (with CDN override already applied)
128156
private def upgrade_direct(product : String, uri : URI) : Nil
129-
dmg_path = File.join(Dir.tempdir, "upgrade-#{product}-#{Time.utc.to_unix}.dmg")
157+
ext = case platform
158+
when "mac" then ".dmg"
159+
when "windows" then ".exe"
160+
else ".tar.gz"
161+
end
162+
dmg_path = File.join(Dir.tempdir, "upgrade-#{product}-#{Time.utc.to_unix}#{ext}")
130163

131164
cdn_uri = uri
132165

jb_updater/src/jb_updater/utils.cr

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,31 @@ module JBUpdater
119119
(s <= b) && (b <= u)
120120
end
121121

122+
# Maps a product name to its JetBrains product code.
123+
#
124+
# Strips trailing version numbers and spaces, matches the base
125+
# name case-insensitively, and returns the short code
126+
# (e.g. `"RubyMine2025.2"` -> `"RM"`, `"phpstorm"` -> `"PS"`).
127+
# Unknown names fall back to the first two uppercase characters.
128+
#
129+
# @param name [String] Product name (e.g. `"RubyMine2025.2"` or `"phpstorm"`)
130+
# @return [String] Product code (e.g. `"RM"`)
131+
def self.product_code(name : String) : String
132+
mapping = {
133+
"rubymine" => "RM",
134+
"webstorm" => "WS",
135+
"pycharm" => "PY",
136+
"clion" => "CL",
137+
"goland" => "GO",
138+
"intellij" => "IU",
139+
"phpstorm" => "PS",
140+
"rider" => "RD",
141+
}
142+
143+
key = name.gsub(/[\d ].*/, "").downcase
144+
mapping[key]? || name[0, 2].upcase
145+
end
146+
122147
# URL-encodes a path segment, replacing `%20` with `+`.
123148
#
124149
# @param str [String] Raw path segment

jb_updater/src/main.cr

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ if opts.list_ide_releases?
2121
end
2222

2323
product_code = opts.product || raise "missing --product"
24+
product_code = JBUpdater::Utils.product_code(product_code)
2425

2526
releases = JBUpdater::IDEReleases.fetch(
2627
product_code,

0 commit comments

Comments
 (0)