Skip to content

Instantly share code, notes, and snippets.

@yinian1992
Created March 6, 2020 07:51
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save yinian1992/e50c6326f20ab22c1e4ebc86969a4ff2 to your computer and use it in GitHub Desktop.
Save yinian1992/e50c6326f20ab22c1e4ebc86969a4ff2 to your computer and use it in GitHub Desktop.
VSCode C# extension offline helper
#!/usr/bin/env python3
#
# VSCode C# extension offline helper
# Version 202003061443
#
# The MIT License (MIT)
#
# Copyright © 2020 yinian@jinkan.org
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the “Software”), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
import argparse
import json
import shutil
import sys
import urllib.request
from pathlib import Path
from zipfile import ZipFile, BadZipFile
FILES = Path('./files')
FILES.mkdir(exist_ok=True)
def main():
platform = (sys.platform)
arch = 'x86_64' if sys.maxsize > 2**32 else 'x86'
parser = argparse.ArgumentParser(description='VSCode C# Extension '
'Offline Helper')
parser.add_argument('-d', '--download', metavar='VERSION',
const='latest', nargs='?', type=str,
help='download specific version (Default: latest)')
parser.add_argument('-i', '--install', metavar='VERSION', nargs=1,
type=str, help='install runtime dependences for '
'specific version')
parser.add_argument('--path', metavar='PATH', nargs=1,
type=str, help='VSCode installation path')
parser.add_argument('--arch', metavar='ARCH', nargs='?', default=arch,
type=str, choices=('x86', 'x86_64'),
help='target architechture')
parser.add_argument('--platform', metavar='PLATFORM', default=platform,
nargs='?', choices=('win32', 'linux', 'darwin'),
type=str, help='target platform')
args = parser.parse_args()
if args.download and args.install:
parser.error('download or install')
if args.download:
if args.download == 'latest':
version = get_latest_version()
print('Latest version of C# extension is "{0}"'.format(version))
else:
version = args.download
extension_file = download_extension(version)
for dep in filter_dependences(extension_file,
args.platform, args.arch):
download_dependence(dep)
if args.install:
if not args.path:
parser.error('need to specify the VSCode installation path')
version = args.install[0]
extension_file = FILES / 'ms-dotnettools.csharp-{0}.vsix' \
.format(version)
print(args.install, args.path)
for dep in filter_dependences(extension_file,
args.platform, args.arch):
install_dependence(dep, args.path[0])
def get_latest_version():
api = 'https://api.github.com/repos/OmniSharp/' \
'omnisharp-vscode/releases/latest'
with urllib.request.urlopen(api) as url:
release = json.loads(url.read().decode())
return release['tag_name'][1:]
def download_file(url, filename):
with urllib.request.urlopen(url) as response, \
open(filename, 'wb') as outfile:
shutil.copyfileobj(response, outfile)
def download_extension(version):
url = 'https://ms-dotnettools.gallery.vsassets.io/_apis/public/gallery/' \
'publisher/ms-dotnettools/extension/csharp/{0}/assetbyname/' \
'Microsoft.VisualStudio.Services.VSIXPackage'.format(version)
print('Downloading C# extension from "{0}"'.format(version))
filename = FILES / 'ms-dotnettools.csharp-{0}.vsix'.format(version)
download_file(url, filename)
return filename
def filter_dependences(extension_file, platform, arch):
with ZipFile(extension_file) as ext:
with ext.open('extension/package.json') as pkg:
pkg_info = json.loads(pkg.read())
return [dep for dep in pkg_info['runtimeDependencies']
if platform in dep['platforms'] and
arch in dep.get('architectures', ['x86', 'x86_64'])]
def get_dependence_filename(dep):
return dep['url'].split('/')[-1]
def download_dependence(dep):
filename = FILES / get_dependence_filename(dep)
print('Downloading {0} from "{1}"'.format(dep['description'], dep['url']))
download_file(dep['url'], filename)
def install_dependence(dep, path):
dep_file = FILES / get_dependence_filename(dep)
destination = Path(path) / dep['installPath']
try:
with ZipFile(dep_file) as f:
print('Extracting {0}...'.format(dep['description']))
f.extractall(destination)
except BadZipFile:
print('Bad zip file: {0}'.format(get_dependence_filename(dep)))
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment