vscode-offline 0.1.3__py3-none-any.whl → 0.1.5__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- vscode_offline/app.py +133 -35
- vscode_offline/download.py +91 -43
- vscode_offline/install.py +37 -17
- vscode_offline/utils.py +39 -21
- {vscode_offline-0.1.3.dist-info → vscode_offline-0.1.5.dist-info}/METADATA +21 -20
- vscode_offline-0.1.5.dist-info/RECORD +11 -0
- vscode_offline-0.1.5.dist-info/WHEEL +4 -0
- {vscode_offline-0.1.3.dist-info → vscode_offline-0.1.5.dist-info}/entry_points.txt +0 -1
- vscode_offline-0.1.3.dist-info/RECORD +0 -11
- vscode_offline-0.1.3.dist-info/WHEEL +0 -4
- {vscode_offline-0.1.3.dist-info → vscode_offline-0.1.5.dist-info}/licenses/LICENSE +0 -0
vscode_offline/app.py
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
3
|
import logging
|
4
|
-
import os
|
5
4
|
from argparse import ArgumentParser, Namespace
|
6
5
|
from pathlib import Path
|
7
6
|
|
8
7
|
from vscode_offline.download import (
|
8
|
+
download_vscode_client,
|
9
9
|
download_vscode_extensions,
|
10
10
|
download_vscode_server,
|
11
11
|
)
|
@@ -14,56 +14,51 @@ from vscode_offline.install import (
|
|
14
14
|
install_vscode_extensions,
|
15
15
|
install_vscode_server,
|
16
16
|
)
|
17
|
-
from vscode_offline.loggers import logger
|
18
17
|
from vscode_offline.utils import (
|
18
|
+
get_default_code_version,
|
19
19
|
get_host_platform,
|
20
|
-
get_vscode_cli_bin,
|
21
|
-
get_vscode_commit_from_code_version,
|
22
|
-
get_vscode_commit_from_server_installer,
|
23
20
|
get_vscode_extensions_config,
|
24
|
-
|
21
|
+
get_vscode_version_from_server_installer,
|
25
22
|
)
|
26
23
|
|
27
24
|
|
28
25
|
def cmd_download_server(args: Namespace) -> None:
|
29
|
-
if args.
|
30
|
-
args.
|
31
|
-
if args.
|
32
|
-
|
33
|
-
"Cannot determine
|
26
|
+
if args.code_version is None:
|
27
|
+
args.code_version = get_default_code_version()
|
28
|
+
if args.code_version is None:
|
29
|
+
raise ValueError(
|
30
|
+
"Cannot determine version from `code --version`, please specify `--version` when downloading."
|
34
31
|
)
|
35
|
-
raise ValueError("Please specify --commit when installing.")
|
36
32
|
|
37
33
|
download_vscode_server(
|
38
|
-
args.
|
39
|
-
output=args.installer / f"server-{args.
|
40
|
-
|
34
|
+
args.code_version,
|
35
|
+
output=args.installer / f"server-{args.code_version.replace(':', '-')}",
|
36
|
+
platform=args.platform,
|
41
37
|
)
|
42
38
|
extensions_config = Path(args.extensions_config).expanduser()
|
43
39
|
download_vscode_extensions(
|
44
|
-
extensions_config,
|
40
|
+
extensions_config,
|
41
|
+
target_platforms=[args.platform],
|
42
|
+
output=args.installer / "extensions",
|
45
43
|
)
|
46
44
|
|
47
45
|
|
48
46
|
def cmd_install_server(args: Namespace) -> None:
|
49
47
|
host_platform = get_host_platform()
|
50
|
-
if args.
|
48
|
+
if args.code_version is None:
|
51
49
|
try:
|
52
|
-
args.
|
50
|
+
args.code_version = get_vscode_version_from_server_installer(
|
53
51
|
args.installer, host_platform
|
54
52
|
)
|
55
53
|
except Exception as e:
|
56
54
|
raise ValueError(
|
57
|
-
f"{e}, please specify `--
|
55
|
+
f"{e}, please specify `--version` when installing."
|
58
56
|
) from None
|
59
57
|
|
60
|
-
install_vscode_server(
|
61
|
-
args.
|
62
|
-
cli_installer=args.installer / f"server-{args.commit}",
|
63
|
-
vscode_cli_bin=get_vscode_cli_bin(args.commit),
|
58
|
+
vscode_server_home = install_vscode_server(
|
59
|
+
server_installer=args.installer / f"server-{args.code_version}",
|
64
60
|
platform=host_platform,
|
65
61
|
)
|
66
|
-
vscode_server_home = get_vscode_server_home(args.commit)
|
67
62
|
install_vscode_extensions(
|
68
63
|
Path(vscode_server_home) / "bin/code-server",
|
69
64
|
vsix_dir=args.installer / "extensions",
|
@@ -75,19 +70,68 @@ def cmd_install_server(args: Namespace) -> None:
|
|
75
70
|
def cmd_download_extensions(args: Namespace) -> None:
|
76
71
|
extensions_config = Path(args.extensions_config).expanduser()
|
77
72
|
download_vscode_extensions(
|
78
|
-
extensions_config,
|
73
|
+
extensions_config,
|
74
|
+
target_platforms=[args.platform],
|
75
|
+
output=args.installer / "extensions",
|
79
76
|
)
|
80
77
|
|
81
78
|
|
82
79
|
def cmd_install_extensions(args: Namespace) -> None:
|
83
80
|
host_platform = get_host_platform()
|
84
81
|
install_vscode_extensions(
|
85
|
-
|
82
|
+
args.code,
|
86
83
|
vsix_dir=args.installer / "extensions",
|
87
84
|
platform=host_platform,
|
88
85
|
)
|
89
86
|
|
90
87
|
|
88
|
+
def cmd_download_client(args: Namespace) -> None:
|
89
|
+
if args.code_version is None:
|
90
|
+
args.code_version = get_default_code_version()
|
91
|
+
if args.code_version is None:
|
92
|
+
raise ValueError(
|
93
|
+
"Cannot determine version from `code --version`, please specify `--version` manually."
|
94
|
+
)
|
95
|
+
|
96
|
+
download_vscode_client(
|
97
|
+
args.code_version,
|
98
|
+
output=args.installer / f"client-{args.code_version.replace(':', '-')}",
|
99
|
+
platform=args.platform,
|
100
|
+
)
|
101
|
+
extensions_config = Path(args.extensions_config).expanduser()
|
102
|
+
download_vscode_extensions(
|
103
|
+
extensions_config,
|
104
|
+
target_platforms=[args.platform],
|
105
|
+
output=args.installer / "extensions",
|
106
|
+
)
|
107
|
+
|
108
|
+
|
109
|
+
def cmd_download_all(args: Namespace) -> None:
|
110
|
+
if args.code_version is None:
|
111
|
+
args.code_version = get_default_code_version()
|
112
|
+
if args.code_version is None:
|
113
|
+
raise ValueError(
|
114
|
+
"Cannot determine version from `code --version`, please specify `--version` manually."
|
115
|
+
)
|
116
|
+
|
117
|
+
download_vscode_server(
|
118
|
+
args.code_version,
|
119
|
+
output=args.installer / f"server-{args.code_version.replace(':', '-')}",
|
120
|
+
platform=args.server_platform,
|
121
|
+
)
|
122
|
+
download_vscode_client(
|
123
|
+
args.code_version,
|
124
|
+
output=args.installer / f"client-{args.code_version.replace(':', '-')}",
|
125
|
+
platform=args.client_platform,
|
126
|
+
)
|
127
|
+
extensions_config = Path(args.extensions_config).expanduser()
|
128
|
+
download_vscode_extensions(
|
129
|
+
extensions_config,
|
130
|
+
target_platforms=[args.server_platform, args.client_platform],
|
131
|
+
output=args.installer / "extensions",
|
132
|
+
)
|
133
|
+
|
134
|
+
|
91
135
|
def make_argparser() -> ArgumentParser:
|
92
136
|
parent_parser = ArgumentParser(add_help=False)
|
93
137
|
|
@@ -108,12 +152,12 @@ def make_argparser() -> ArgumentParser:
|
|
108
152
|
)
|
109
153
|
download_server_parser.set_defaults(func=cmd_download_server)
|
110
154
|
download_server_parser.add_argument(
|
111
|
-
"--
|
155
|
+
"--code-version",
|
112
156
|
type=str,
|
113
|
-
help="The
|
157
|
+
help="The version of the VS Code Server to download, must match the version of the VS Code Client.",
|
114
158
|
)
|
115
159
|
download_server_parser.add_argument(
|
116
|
-
"--
|
160
|
+
"--platform",
|
117
161
|
type=str,
|
118
162
|
required=True,
|
119
163
|
help="The target platform of the VS Code Server to download.",
|
@@ -132,9 +176,9 @@ def make_argparser() -> ArgumentParser:
|
|
132
176
|
)
|
133
177
|
install_server_parser.set_defaults(func=cmd_install_server)
|
134
178
|
install_server_parser.add_argument(
|
135
|
-
"--
|
179
|
+
"--code-version",
|
136
180
|
type=str,
|
137
|
-
help="The
|
181
|
+
help="The version of the VS Code Server to install.",
|
138
182
|
)
|
139
183
|
|
140
184
|
download_extensions_parser = subparsers.add_parser(
|
@@ -144,10 +188,10 @@ def make_argparser() -> ArgumentParser:
|
|
144
188
|
)
|
145
189
|
download_extensions_parser.set_defaults(func=cmd_download_extensions)
|
146
190
|
download_extensions_parser.add_argument(
|
147
|
-
"--
|
191
|
+
"--platform",
|
148
192
|
type=str,
|
149
193
|
required=True,
|
150
|
-
help="The target platform of the
|
194
|
+
help="The target platform of the VS Code extensions to download.",
|
151
195
|
)
|
152
196
|
download_extensions_parser.add_argument(
|
153
197
|
"--extensions-config",
|
@@ -158,17 +202,71 @@ def make_argparser() -> ArgumentParser:
|
|
158
202
|
|
159
203
|
install_extensions_parser = subparsers.add_parser(
|
160
204
|
"install-extensions",
|
161
|
-
help="Install
|
205
|
+
help="Install VS Code extensions",
|
162
206
|
parents=[parent_parser],
|
163
207
|
)
|
164
208
|
install_extensions_parser.set_defaults(func=cmd_install_extensions)
|
165
|
-
|
209
|
+
install_extensions_parser.add_argument(
|
166
210
|
"--code",
|
167
211
|
type=str,
|
168
212
|
default="code",
|
169
213
|
help="Path to the `code` binary.",
|
170
214
|
)
|
171
215
|
|
216
|
+
download_client_parser = subparsers.add_parser(
|
217
|
+
"download-client",
|
218
|
+
help="Download VS Code and extensions",
|
219
|
+
parents=[parent_parser],
|
220
|
+
)
|
221
|
+
download_client_parser.set_defaults(func=cmd_download_client)
|
222
|
+
download_client_parser.add_argument(
|
223
|
+
"--code-version",
|
224
|
+
type=str,
|
225
|
+
help="The version of the VS Code to download, must match the version of the VS Code Client.",
|
226
|
+
)
|
227
|
+
download_client_parser.add_argument(
|
228
|
+
"--platform",
|
229
|
+
type=str,
|
230
|
+
required=True,
|
231
|
+
help="The target platform of the VS Code to download.",
|
232
|
+
)
|
233
|
+
download_client_parser.add_argument(
|
234
|
+
"--extensions-config",
|
235
|
+
type=Path,
|
236
|
+
default=get_vscode_extensions_config(),
|
237
|
+
help="Path to the extensions configuration file. Will search for extensions to download.",
|
238
|
+
)
|
239
|
+
|
240
|
+
download_all_parser = subparsers.add_parser(
|
241
|
+
"download-all",
|
242
|
+
help="Download VS Code server, client and extensions, all in one command",
|
243
|
+
parents=[parent_parser],
|
244
|
+
)
|
245
|
+
download_all_parser.set_defaults(func=cmd_download_all)
|
246
|
+
download_all_parser.add_argument(
|
247
|
+
"--code-version",
|
248
|
+
type=str,
|
249
|
+
help="The version of the VS Code to download, defaults to `code --version` at current environment.",
|
250
|
+
)
|
251
|
+
download_all_parser.add_argument(
|
252
|
+
"--server-platform",
|
253
|
+
type=str,
|
254
|
+
default="linux-x64",
|
255
|
+
help="The target platform of the VS Code Server to download, defaults to linux-x64.",
|
256
|
+
)
|
257
|
+
download_all_parser.add_argument(
|
258
|
+
"--client-platform",
|
259
|
+
type=str,
|
260
|
+
default="win32-x64",
|
261
|
+
help="The target platform of the VS Code to download, defaults to win32-x64.",
|
262
|
+
)
|
263
|
+
download_all_parser.add_argument(
|
264
|
+
"--extensions-config",
|
265
|
+
type=Path,
|
266
|
+
default=get_vscode_extensions_config(),
|
267
|
+
help="Path to the extensions configuration file. Will search for extensions to download.",
|
268
|
+
)
|
269
|
+
|
172
270
|
return parser
|
173
271
|
|
174
272
|
|
vscode_offline/download.py
CHANGED
@@ -2,17 +2,22 @@ from __future__ import annotations
|
|
2
2
|
|
3
3
|
import json
|
4
4
|
import os
|
5
|
+
from collections.abc import Sequence
|
5
6
|
from gzip import GzipFile
|
7
|
+
from io import DEFAULT_BUFFER_SIZE
|
8
|
+
from pathlib import Path
|
6
9
|
from urllib.error import HTTPError
|
7
10
|
from urllib.request import urlopen
|
8
11
|
|
9
12
|
from vscode_offline.loggers import logger
|
10
|
-
from vscode_offline.utils import
|
13
|
+
from vscode_offline.utils import get_cli_platform, get_filename_from_headers
|
11
14
|
|
12
|
-
_CHUCK_SIZE = 4 * 1024 * 1024 # 4MiB
|
13
15
|
|
14
|
-
|
15
|
-
|
16
|
+
def _download_file(
|
17
|
+
url: str,
|
18
|
+
directory: str | os.PathLike[str],
|
19
|
+
filename: str | None = None,
|
20
|
+
) -> os.PathLike[str]:
|
16
21
|
with urlopen(url) as resp:
|
17
22
|
content_encoding = resp.headers.get("Content-Encoding")
|
18
23
|
if content_encoding in {"gzip", "deflate"}:
|
@@ -23,39 +28,56 @@ def _download_file(url: str, filename: str) -> None:
|
|
23
28
|
else:
|
24
29
|
raise ValueError(f"Unsupported Content-Encoding: {content_encoding}")
|
25
30
|
|
26
|
-
|
31
|
+
if filename:
|
32
|
+
file_path = Path(directory).joinpath(filename)
|
33
|
+
else:
|
34
|
+
filename = get_filename_from_headers(resp.headers)
|
35
|
+
if not filename:
|
36
|
+
raise ValueError(
|
37
|
+
"Cannot get filename from HTTP headers, please specify argument `filename`."
|
38
|
+
)
|
39
|
+
logger.info(f"Get filename `{filename}` from HTTP headers.")
|
40
|
+
file_path = Path(directory).joinpath(filename)
|
41
|
+
if file_path.exists():
|
42
|
+
logger.info(f"File {file_path} already exists, skipping download.")
|
43
|
+
return file_path
|
44
|
+
|
45
|
+
tmp_file_path = Path(directory).joinpath(f"{filename}.tmp")
|
46
|
+
with reader, tmp_file_path.open("wb") as fp:
|
27
47
|
while True:
|
28
|
-
chunk = reader.read(
|
48
|
+
chunk = reader.read(DEFAULT_BUFFER_SIZE)
|
29
49
|
if not chunk:
|
30
50
|
break
|
31
51
|
fp.write(chunk)
|
32
52
|
|
53
|
+
if os.path.exists(file_path):
|
54
|
+
os.remove(file_path)
|
55
|
+
os.rename(tmp_file_path, file_path)
|
56
|
+
|
57
|
+
logger.info(f"Saved to {file_path} .")
|
58
|
+
return file_path
|
59
|
+
|
33
60
|
|
34
61
|
def download_file(
|
35
62
|
url: str,
|
36
|
-
|
63
|
+
directory: str | os.PathLike[str],
|
64
|
+
filename: str | None = None,
|
37
65
|
) -> None:
|
38
|
-
if
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
tmp_filename = f"{filename}.tmp"
|
66
|
+
if filename:
|
67
|
+
file_path = Path(directory).joinpath(filename)
|
68
|
+
if file_path.exists():
|
69
|
+
logger.info(f"File {file_path} already exists, skipping download.")
|
70
|
+
return
|
44
71
|
|
72
|
+
logger.info(f"Downloading {url} ...")
|
45
73
|
for i in range(3):
|
46
74
|
try:
|
47
|
-
_download_file(url,
|
75
|
+
_download_file(url, directory, filename)
|
48
76
|
break
|
49
77
|
except Exception as e:
|
50
78
|
if isinstance(e, HTTPError) and e.code == 404:
|
51
79
|
raise
|
52
|
-
logger.info(f"Attempt {i + 1} failed: {e}")
|
53
|
-
|
54
|
-
if os.path.exists(filename):
|
55
|
-
os.remove(filename)
|
56
|
-
os.rename(tmp_filename, filename)
|
57
|
-
|
58
|
-
logger.info(f"Saved to {filename}")
|
80
|
+
logger.info(f"Attempt {i + 1} times failed: {e}")
|
59
81
|
|
60
82
|
|
61
83
|
def download_extension(
|
@@ -68,15 +90,14 @@ def download_extension(
|
|
68
90
|
url = f"https://marketplace.visualstudio.com/_apis/public/gallery/publishers/{publisher}/vsextensions/{name}/{version}/vspackage"
|
69
91
|
if platform:
|
70
92
|
url = f"{url}?targetPlatform={platform}"
|
71
|
-
filename = f"{publisher}.{name}-{version}"
|
72
|
-
|
73
|
-
filename = f"{filename}@{platform}"
|
74
|
-
filename = f"{filename}.vsix"
|
75
|
-
download_file(url, f"{output}/{filename}")
|
93
|
+
filename = f"{publisher}.{name}-{version}{f'@{platform}' if platform else ''}.vsix"
|
94
|
+
download_file(url, output, filename)
|
76
95
|
|
77
96
|
|
78
97
|
def download_vscode_extensions(
|
79
|
-
extensions_config: os.PathLike[str],
|
98
|
+
extensions_config: os.PathLike[str],
|
99
|
+
target_platforms: Sequence[str],
|
100
|
+
output: str = ".",
|
80
101
|
) -> None:
|
81
102
|
logger.info(f"Reading extensions config from {extensions_config}")
|
82
103
|
with open(extensions_config) as fp:
|
@@ -87,18 +108,42 @@ def download_vscode_extensions(
|
|
87
108
|
identifier = extension["identifier"]
|
88
109
|
publisher, name = identifier["id"].split(".")
|
89
110
|
version = extension["version"]
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
111
|
+
|
112
|
+
requires_fallback_download = False
|
113
|
+
for target_platform in target_platforms:
|
114
|
+
try:
|
115
|
+
download_extension(
|
116
|
+
publisher, name, version, target_platform, output=output
|
117
|
+
)
|
118
|
+
except HTTPError as e:
|
119
|
+
if e.code == 404:
|
120
|
+
requires_fallback_download = True
|
121
|
+
continue
|
94
122
|
raise
|
123
|
+
if requires_fallback_download:
|
95
124
|
download_extension(publisher, name, version, output=output)
|
96
125
|
|
97
126
|
|
127
|
+
def _download_vscode(
|
128
|
+
version: str,
|
129
|
+
output: str,
|
130
|
+
platform: str,
|
131
|
+
) -> None:
|
132
|
+
"""Download VS Code for the given version and target platform."""
|
133
|
+
|
134
|
+
# filename is like
|
135
|
+
# "VS CodeSetup-x64-1.104.3.exe" for windows VS Code,
|
136
|
+
# "vscode-server-linux-x64.tar.gz" for linux VS Code Server,
|
137
|
+
# "vscode_cli_alpine_x64_cli.tar.gz" for linux VS Code CLI.
|
138
|
+
download_file(
|
139
|
+
f"https://update.code.visualstudio.com/{version}/{platform}/stable", output
|
140
|
+
)
|
141
|
+
|
142
|
+
|
98
143
|
def download_vscode_server(
|
99
|
-
|
144
|
+
version: str,
|
100
145
|
output: str,
|
101
|
-
|
146
|
+
platform: str,
|
102
147
|
) -> None:
|
103
148
|
"""Download VS Code Server and CLI for the given commit and target platform.
|
104
149
|
|
@@ -107,13 +152,16 @@ def download_vscode_server(
|
|
107
152
|
https://blog.csdn.net/qq_69668825/article/details/144224417
|
108
153
|
"""
|
109
154
|
os.makedirs(output, exist_ok=True)
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
155
|
+
_download_vscode(version, output, f"server-{platform}")
|
156
|
+
cli_platform = get_cli_platform(platform)
|
157
|
+
_download_vscode(version, output, f"cli-{cli_platform}")
|
158
|
+
|
159
|
+
|
160
|
+
def download_vscode_client(
|
161
|
+
version: str,
|
162
|
+
output: str,
|
163
|
+
platform: str,
|
164
|
+
) -> None:
|
165
|
+
"""Download VS Code Client for the given version and target platform."""
|
166
|
+
os.makedirs(output, exist_ok=True)
|
167
|
+
_download_vscode(version, output, platform)
|
vscode_offline/install.py
CHANGED
@@ -1,13 +1,18 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
3
|
import os
|
4
|
+
import re
|
4
5
|
import subprocess
|
5
6
|
from collections.abc import Set as AbstractSet
|
6
7
|
from pathlib import Path
|
7
8
|
from tempfile import TemporaryDirectory
|
8
9
|
|
9
10
|
from vscode_offline.loggers import logger
|
10
|
-
from vscode_offline.utils import
|
11
|
+
from vscode_offline.utils import (
|
12
|
+
get_cli_platform,
|
13
|
+
get_vscode_cli_bin,
|
14
|
+
get_vscode_server_home,
|
15
|
+
)
|
11
16
|
|
12
17
|
# These extensions are excluded because they are not needed in a VS Code Server.
|
13
18
|
SERVER_EXCLUDE_EXTENSIONS = frozenset(
|
@@ -64,26 +69,40 @@ def install_vscode_extensions(
|
|
64
69
|
logger.info(f"Installed {vsix_file}")
|
65
70
|
|
66
71
|
|
72
|
+
_code_version_output_pattern = re.compile(rb"\(commit ([0-9a-f]{40,})\)")
|
73
|
+
|
74
|
+
|
75
|
+
def _extract_commit_from_code_version_output(code_version_output: bytes) -> str:
|
76
|
+
m = _code_version_output_pattern.search(code_version_output)
|
77
|
+
if not m:
|
78
|
+
raise ValueError("Cannot determine commit hash from code version")
|
79
|
+
return m.group(1).decode("utf-8")
|
80
|
+
|
81
|
+
|
67
82
|
def install_vscode_server(
|
68
|
-
|
69
|
-
cli_installer: str,
|
70
|
-
vscode_cli_bin: os.PathLike[str],
|
83
|
+
server_installer: str,
|
71
84
|
platform: str,
|
72
|
-
) ->
|
73
|
-
|
74
|
-
|
85
|
+
) -> os.PathLike[str]:
|
86
|
+
cli_platform = get_cli_platform(platform)
|
87
|
+
cli_platform_ = cli_platform.replace("-", "_")
|
75
88
|
|
76
|
-
|
89
|
+
code_cli_tarball = Path(server_installer) / f"vscode_cli_{cli_platform_}_cli.tar.gz"
|
77
90
|
with TemporaryDirectory() as tmpdir:
|
78
|
-
subprocess.check_call(["tar", "-xzf",
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
91
|
+
subprocess.check_call(["tar", "-xzf", code_cli_tarball, "-C", tmpdir])
|
92
|
+
tmp_code_cli = Path(tmpdir) / "code"
|
93
|
+
version_output = subprocess.check_output(
|
94
|
+
["code", "--version"], executable=tmp_code_cli, cwd=tmpdir
|
95
|
+
)
|
96
|
+
commit = _extract_commit_from_code_version_output(version_output)
|
97
|
+
logger.info(f"Extracted commit from `code --version`: {commit}")
|
98
|
+
code_cli = get_vscode_cli_bin(commit)
|
99
|
+
if os.path.exists(code_cli):
|
100
|
+
os.remove(code_cli)
|
101
|
+
os.makedirs(os.path.dirname(code_cli), exist_ok=True)
|
102
|
+
os.rename(tmp_code_cli, code_cli)
|
103
|
+
logger.info(f"Extracted vscode_cli_{cli_platform_}_cli.tar.gz to {code_cli}")
|
104
|
+
|
105
|
+
vscode_server_tarball = Path(server_installer) / f"vscode-server-{platform}.tar.gz"
|
87
106
|
vscode_server_home = get_vscode_server_home(commit)
|
88
107
|
os.makedirs(vscode_server_home, exist_ok=True)
|
89
108
|
subprocess.check_call(
|
@@ -97,3 +116,4 @@ def install_vscode_server(
|
|
97
116
|
]
|
98
117
|
)
|
99
118
|
logger.info(f"Extracted vscode-server-{platform}.tar.gz to {vscode_server_home}")
|
119
|
+
return vscode_server_home
|
vscode_offline/utils.py
CHANGED
@@ -4,6 +4,8 @@ import os
|
|
4
4
|
import shutil
|
5
5
|
import subprocess
|
6
6
|
import sys
|
7
|
+
from collections.abc import Mapping
|
8
|
+
from email.parser import HeaderParser
|
7
9
|
from pathlib import Path
|
8
10
|
|
9
11
|
from vscode_offline.loggers import logger
|
@@ -30,7 +32,7 @@ def get_vscode_extensions_config() -> os.PathLike[str]:
|
|
30
32
|
return p # default to this path
|
31
33
|
|
32
34
|
|
33
|
-
def
|
35
|
+
def get_vscode_version_from_server_installer(
|
34
36
|
installer: os.PathLike[str], platform: str
|
35
37
|
) -> str:
|
36
38
|
directories = list(
|
@@ -45,14 +47,16 @@ def get_vscode_commit_from_server_installer(
|
|
45
47
|
f"No matching installer found in {installer} for platform {platform}"
|
46
48
|
)
|
47
49
|
|
48
|
-
|
49
|
-
logger.info(f"Getting
|
50
|
-
return
|
50
|
+
version = directories[0].parent.name[len("server-") :]
|
51
|
+
logger.info(f"Getting version from {platform} installer: {version}")
|
52
|
+
return version
|
51
53
|
|
52
54
|
|
53
|
-
def
|
54
|
-
"""Get the current VS Code
|
55
|
-
|
55
|
+
def get_default_code_version() -> str | None:
|
56
|
+
"""Get the current VS Code version by running `code --version`.
|
57
|
+
|
58
|
+
Returns:
|
59
|
+
`None` if `code` is not found or the output is unexpected.
|
56
60
|
"""
|
57
61
|
executable = shutil.which("code")
|
58
62
|
if executable is None:
|
@@ -70,34 +74,30 @@ def get_vscode_commit_from_code_version() -> str | None:
|
|
70
74
|
|
71
75
|
# The commit hash is usually on the second line
|
72
76
|
commit = lines[1].strip().decode("utf-8")
|
73
|
-
|
74
|
-
|
75
|
-
return lines[1].strip().decode("utf-8")
|
77
|
+
version = f"commit:{commit}"
|
78
|
+
logger.info(f"Getting version from `code --version`: {version}")
|
76
79
|
|
77
|
-
|
78
|
-
def get_target_platform_from_installer(cli_installer: str) -> str | None:
|
79
|
-
directories = list(Path(cli_installer).glob("vscode-server-*.tar.gz"))
|
80
|
-
if len(directories) == 1:
|
81
|
-
return directories[0].name[len("vscode-server-") : -len(".tar.gz")]
|
82
|
-
return None
|
80
|
+
return version
|
83
81
|
|
84
82
|
|
85
83
|
# Mapping from target platform to CLI OS and architecture used in download URLs
|
86
|
-
|
84
|
+
_cli_platform_mapping = {
|
87
85
|
"linux-x64": "alpine-x64",
|
88
86
|
"linux-arm64": "alpine-arm64",
|
87
|
+
"linux-armhf": "linux-arm64",
|
88
|
+
"win32-x64": "win32-x64",
|
89
89
|
}
|
90
90
|
|
91
91
|
|
92
|
-
def
|
92
|
+
def get_cli_platform(platform: str) -> str:
|
93
93
|
"""Get the CLI OS and architecture for the given target platform."""
|
94
|
-
if platform not in
|
94
|
+
if platform not in _cli_platform_mapping:
|
95
95
|
raise ValueError(f"Unsupported target platform: {platform}")
|
96
|
-
return
|
96
|
+
return _cli_platform_mapping[platform]
|
97
97
|
|
98
98
|
|
99
99
|
def get_host_platform() -> str:
|
100
|
-
"""Get the host platform in the format used by VS Code
|
100
|
+
"""Get the host platform in the format used by VS Code install."""
|
101
101
|
if os.name == "nt":
|
102
102
|
if "amd64" in sys.version.lower():
|
103
103
|
return "win32-x64"
|
@@ -111,3 +111,21 @@ def get_host_platform() -> str:
|
|
111
111
|
elif machine in ("aarch64", "arm64"):
|
112
112
|
return "linux-arm64"
|
113
113
|
raise ValueError(f"Unsupported host platform: {osname}-{machine}")
|
114
|
+
|
115
|
+
|
116
|
+
def get_filename_from_headers(headers: Mapping[str, str]) -> str | None:
|
117
|
+
"""Get the filename from HTTP headers.
|
118
|
+
|
119
|
+
Args:
|
120
|
+
headers: The HTTP headers.
|
121
|
+
"""
|
122
|
+
content_disposition = headers.get("Content-Disposition")
|
123
|
+
header_str = ""
|
124
|
+
if content_type := headers.get("Content-Type"):
|
125
|
+
header_str += f"Content-Type: {content_type}\n"
|
126
|
+
if content_disposition := headers.get("Content-Disposition"):
|
127
|
+
header_str += f"Content-Disposition: {content_disposition}\n"
|
128
|
+
if not header_str:
|
129
|
+
return None
|
130
|
+
header = HeaderParser().parsestr(header_str)
|
131
|
+
return header.get_filename()
|
@@ -1,25 +1,24 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: vscode-offline
|
3
|
-
Version: 0.1.
|
3
|
+
Version: 0.1.5
|
4
4
|
Summary: Download and install VS Code Server for offline environments
|
5
|
-
|
5
|
+
Project-URL: Homepage, https://github.com/fanck0605/vscode-offline
|
6
6
|
Author-email: Chuck Fan <fanck0605@qq.com>
|
7
7
|
License-Expression: MIT
|
8
8
|
License-File: LICENSE
|
9
9
|
Classifier: Development Status :: 4 - Beta
|
10
|
+
Classifier: Intended Audience :: Developers
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
12
|
+
Classifier: Operating System :: Microsoft :: Windows
|
13
|
+
Classifier: Operating System :: POSIX :: Linux
|
10
14
|
Classifier: Programming Language :: Python :: 3 :: Only
|
11
15
|
Classifier: Programming Language :: Python :: 3.9
|
12
16
|
Classifier: Programming Language :: Python :: 3.10
|
13
17
|
Classifier: Programming Language :: Python :: 3.11
|
14
18
|
Classifier: Programming Language :: Python :: 3.12
|
15
19
|
Classifier: Programming Language :: Python :: 3.13
|
16
|
-
Classifier: Intended Audience :: Developers
|
17
|
-
Classifier: License :: OSI Approved :: MIT License
|
18
|
-
Classifier: Operating System :: POSIX :: Linux
|
19
|
-
Classifier: Operating System :: Microsoft :: Windows
|
20
20
|
Classifier: Topic :: Utilities
|
21
21
|
Requires-Python: >=3.9
|
22
|
-
Project-URL: Homepage, https://github.com/fanck0605/vscode-offline
|
23
22
|
Description-Content-Type: text/markdown
|
24
23
|
|
25
24
|
# vscode-offline
|
@@ -37,38 +36,40 @@ pip install -U vscode-offline
|
|
37
36
|
1. 自动识别并下载所有 `.vsix` 文件(包括间接依赖)
|
38
37
|
2. 一键安装 VS Code Server 以及所有插件
|
39
38
|
|
40
|
-
## VS Code
|
39
|
+
## VS Code 离线安装
|
41
40
|
|
42
|
-
(1)在联网环境安装好
|
41
|
+
(1)在联网环境安装好 VS Code 和你需要的插件,如 [Remote - SSH](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-ssh), [Python](https://marketplace.visualstudio.com/items?itemName=ms-python.python) 等。
|
43
42
|
|
44
|
-
(2
|
43
|
+
(2)执行如下命令,下载 VS Code 安装包,和目前安装的所有的插件
|
45
44
|
|
46
|
-
|
47
|
-
|
48
|
-
|
45
|
+
```shell
|
46
|
+
vscode-offline download-all --installer ./vscode-offline-installer
|
47
|
+
```
|
48
|
+
|
49
|
+
(3)复制 `./vscode-offline-installer` 到内网 Windows 机器,安装 `client-<version>` 下的 VS Code,然后执行如下命令安装所有插件
|
49
50
|
|
50
51
|
```shell
|
51
|
-
vscode-offline
|
52
|
+
vscode-offline install-extensions --installer ./vscode-offline-installer
|
52
53
|
```
|
53
54
|
|
54
|
-
(
|
55
|
+
(4)复制 `./vscode-offline-installer` 到内网 Linux 服务器,执行如下命令安装 VS Code Server 和所有插件
|
55
56
|
|
56
57
|
```shell
|
57
58
|
vscode-offline install-server --installer ./vscode-offline-installer
|
58
59
|
```
|
59
60
|
|
60
|
-
## VS Code
|
61
|
+
## 指定 VS Code 版本号
|
61
62
|
|
62
|
-
|
63
|
+
如果你想下载或安装指定版本的 VS Code,可以先通过 `code --version` 获取当前版本,然后通过 --code-version 参数指定版本号,例如:
|
63
64
|
|
64
65
|
```shell
|
65
|
-
vscode-offline download-
|
66
|
+
vscode-offline download-all --code-version 1.104.3
|
66
67
|
```
|
67
68
|
|
68
|
-
|
69
|
+
也支持使用 commit hash 作为版本号,例如:
|
69
70
|
|
70
71
|
```shell
|
71
|
-
vscode-offline
|
72
|
+
vscode-offline download-all --code-version commit:385651c938df8a906869babee516bffd0ddb9829
|
72
73
|
```
|
73
74
|
|
74
75
|
## 贡献
|
@@ -0,0 +1,11 @@
|
|
1
|
+
vscode_offline/__init__.py,sha256=dr6Jtj0XT9eQEC4fzNigEYsAIEfCsaom3HDbUsS-2O4,57
|
2
|
+
vscode_offline/app.py,sha256=Rm-uSkJrRmJVmyjiubPpRQTofttQdTADsGyEeZDSeY0,9142
|
3
|
+
vscode_offline/download.py,sha256=blu2WjG8zhuBrHrrV-v-FUf2fXbIt7fVSX8DoUX_Ikk,5442
|
4
|
+
vscode_offline/install.py,sha256=SZ3UTg-fkzJrfwMGyvjyayyGHGKbymm6bkUs0ayZsKQ,4214
|
5
|
+
vscode_offline/loggers.py,sha256=vX91NMtNo1xfxq5y4BCtm_uhCTKtCODqBJHNvcT7JdQ,104
|
6
|
+
vscode_offline/utils.py,sha256=vpVSmE4JN0O-ezpzNJo9R9Y3YjzmzslhRVTG5H_-We0,4038
|
7
|
+
vscode_offline-0.1.5.dist-info/METADATA,sha256=-ssOm2fHDRr25iLf5hcm4G7BYbMhrwobwrouzwuOWMY,2803
|
8
|
+
vscode_offline-0.1.5.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
9
|
+
vscode_offline-0.1.5.dist-info/entry_points.txt,sha256=XyuZLe7bgm2RmZp9oh9qCxcrAwHypD8XrTnm4G0_CzM,55
|
10
|
+
vscode_offline-0.1.5.dist-info/licenses/LICENSE,sha256=pUIXFkLeTS986b7dopOVLyuw72fJsUxhl8H3rEMIycA,1053
|
11
|
+
vscode_offline-0.1.5.dist-info/RECORD,,
|
@@ -1,11 +0,0 @@
|
|
1
|
-
vscode_offline/__init__.py,sha256=dr6Jtj0XT9eQEC4fzNigEYsAIEfCsaom3HDbUsS-2O4,57
|
2
|
-
vscode_offline/app.py,sha256=MLYg_2q3pudTL0zBAWTRSjVM_8zDccLIZt0s00xvT7Q,5686
|
3
|
-
vscode_offline/download.py,sha256=hImUW23npX0hgNMcJ2E3nRLbW8fvzeIFEovg0mWzm9U,3763
|
4
|
-
vscode_offline/install.py,sha256=UNgLnMAAZEOtX4s8v6QlmuIHPf3aeuXrCWfC6_TEM-0,3488
|
5
|
-
vscode_offline/loggers.py,sha256=vX91NMtNo1xfxq5y4BCtm_uhCTKtCODqBJHNvcT7JdQ,104
|
6
|
-
vscode_offline/utils.py,sha256=XknMz1aBMyyTvWG-szE_0eMG3KOdpl9aAeX3E5BrDAI,3542
|
7
|
-
vscode_offline-0.1.3.dist-info/licenses/LICENSE,sha256=pUIXFkLeTS986b7dopOVLyuw72fJsUxhl8H3rEMIycA,1053
|
8
|
-
vscode_offline-0.1.3.dist-info/WHEEL,sha256=-neZj6nU9KAMg2CnCY6T3w8J53nx1kFGw_9HfoSzM60,79
|
9
|
-
vscode_offline-0.1.3.dist-info/entry_points.txt,sha256=zIMeh_ENKKzlt9lDao8icofSI0TeCQxH8eCwxIRI2G8,56
|
10
|
-
vscode_offline-0.1.3.dist-info/METADATA,sha256=hjeP62mRkggLHpIypE8RgSNXUceC9PYGYBWCrhvabak,2492
|
11
|
-
vscode_offline-0.1.3.dist-info/RECORD,,
|
File without changes
|