La mise à jour de PowerShell sous Windows est un processus manuel, mais il est devenu ennuyeux. Semi-automatisé.
Si vous exécutez pshupdate.py, qui est le corps principal du script, en python, il vérifiera automatiquement la version et la mettra à jour si nécessaire (prévu).
Je ne fais pas cela de manière élaborée parce que je l'ai simplement écrit selon ce que je devrais faire, mais je vais l'écrire.
Puisque la version est une chaîne de caractères de la forme "v3.2.1", le reste après avoir supprimé seulement le premier caractère supplémentaire est transformé en un triplet de chaînes de caractères pour en faire un type Version. Si vous faites (Get-Host) .Version | Format-List *
avec pwsh, vous obtiendrez 6 clés, mais je n'ai pas utilisé les dernières 3 et 3 suffisent! Yoshi!
Une comparaison grande et petite est mise en œuvre dans la seconde moitié. Pour une certaine flexibilité, j'ai décidé de traiter None, 0 et -1 comme les valeurs de Minor
et Build
, ce qui a rendu le résultat ennuyeux.
import re
from typing import NamedTuple
class Version(NamedTuple):
"""A triplet (Major, Minor, Build) of strings.
Note that every element can have the None value.
"""
Major: str
Minor: str = '-1'
Build: str = '-1'
def formatVersion(self) ->str:
"""Return a string "<Major>.<Minor>.<Build>".\n
Note that this function returns None if Version is None.
"""
if (self.Major is None):
return None
else:
ls = [self.Major]
if (self.Minor != None) and (self.Minor != '-1'):
ls.append(self.Minor)
if (self.Build != None) and (self.Build != '-1'):
ls.append(self.Build)
return '.'.join(ls)
def __eq__(self,other) -> bool:
if(not isinstance(other,Version)):
raise TypeError("Version data cannot compare to other type data.")
if(self.Major != other.Major):
return False
elif(not self.Major):
return True
elif(self.Minor != other.Minor):
if (self.Minor != None) and (self.Minor != '0') and (self.Minor != '-1'):
return False
elif (other.Minor != None) and (other.Minor != '0') and (other.Minor != '-1'):
return False
elif(self.Build != other.Build):
if (self.Build != None) and (self.Build != '0') and (self.Build != '-1'):
return False
elif (other.Build != None) and (other.Build != '0') and (other.Build != '-1'):
return False
else:
return True
def __le__(self,other) -> bool:
if(not isinstance(other,Version)):
raise TypeError("Version data cannot compare to other type data.")
if(self.Major.isdecimal()) and (other.Major.isdecimal()):
if(int(self.Major) < int(other.Major)):
return True
else:
pass
elif(not self.Major.isdecimal()) and (not other.Major.isdecimal()):
a, b = self.Major, other.Major
mslf = re.search(r'^\d*',a)
if mslf:
anum, atxt = a[:mslf.end()], a[mslf.end():]
else:
anum, atxt = None, a
moth = re.search(r'^\d*',b)
if moth:
bnum, btxt = b[:moth.end()], b[moth.end():]
else:
bnum, btxt = None, b
if(int(anum) < int(bnum)):
return True
elif(int(anum)==int(bnum)):
if(atxt < btxt):
return True
elif(atxt == btxt):
pass
else:
return False
else:
return False
else:
raise ValueError("two Version data are not compareable.")
if(self.Minor.isdecimal()) and (other.Minor.isdecimal()):
if(int(self.Minor) < int(other.Minor)):
return True
else:
pass
elif(not self.Minor.isdecimal()) and (not other.Minor.isdecimal()):
a, b = self.Minor, other.Minor
mslf = re.search(r'^\d*',a)
if mslf:
anum, atxt = a[:mslf.end()], a[mslf.end():]
else:
anum, atxt = None, a
moth = re.search(r'^\d*',b)
if moth:
bnum, btxt = b[:moth.end()], b[moth.end():]
else:
bnum, btxt = None, b
if(int(anum) < int(bnum)):
return True
elif(int(anum)==int(bnum)):
if(atxt < btxt):
return True
elif(atxt == btxt):
pass
else:
return False
else:
return False
else:
raise ValueError("two Version data are not compareable.")
if(self.Build.isdecimal()) and (other.Build.isdecimal()):
if(int(self.Build) < int(other.Build)):
return True
else:
return False
elif(not self.Build.isdecimal()) and (other.Build.isdecimal()):
a, b = self.Build, other.Build
mslf = re.search(r'^\d*',a)
if mslf:
anum, atxt = a[:mslf.end()], a[mslf.end():]
else:
anum, atxt = None, a
moth = re.search(r'^\d*',b)
if moth:
bnum, btxt = b[:moth.end()], b[moth.end():]
else:
bnum, btxt = None, b
if(int(anum) < int(bnum)):
return True
elif(int(anum)==int(bnum)):
if(atxt < btxt):
return True
else:
return False
else:
return False
else:
raise ValueError("two Version data are not compareable.")
J'ai également créé une fonction qui analyse une chaîne dans un type Version.
def parseVersion(s) ->Version:
"""input: a string or a list of strings
Parse a string like "v1.0.4"
"""
if(isinstance(s,bytes)):
s = s.decode()
if(isinstance(s,str)):
match = re.search(r'\d*\.\d*\.?',s)
if match:
token = s[match.start():].split('.',2)
if (len(token)==3):
return Version(token[0],token[1],token[2])
elif (len(token)==2):
return Version(token[0],token[1],None)
else:
return Version(token[0],None,None)
else:
match = re.search(r'[0-9]*',s)
if match:
return Version(s[match.start():],None,None)
else:
raise ValueError("function parseVersion didn't parse argument.")
elif(isinstance(s,list)):
for x in s[0:3]:
if(not isinstance(x,str)):
raise TypeError("function parseVersion(s) takes a string or a list of strings as the argument.")
try:
(major,minor,build) = s[0:3]
except ValueError:
raise
except:
print("Unexpected error in function 'parseVersion'")
raise
return Version(major,minor,build)
else:
raise TypeError("function parseVersion(s) takes a string or a list of strings as the argument.")
Accédez à l'API Github pour obtenir des informations sur la dernière version. ''
import ssl, urllib.request
import json
head_accept = "application/vnd.github.v3+json"
host = "api.github.com"
key_release = "repos/PowerShell/PowerShell/releases/latest"
print("Latest version of pwsh ... ", end='', flush=True)
context = ssl.create_default_context()
url = ''.join(["https://",host,"/",key_release])
try:
q = urllib.request.Request(url,headers={'accept':head_accept},method='GET')
with urllib.request.urlopen(q, context=context) as res:
content = json.load(res)
except urllib.error.HTTPError as err:
print(err.code)
except urllib.error.URLError as err:
print(err.reason)
except json.JSONDecodeError as err:
print(err.msg)
v_latest = parseVersion(content['tag_name'])
print(v.formatVersion())
Obtenez les informations de version avec pwsh -v
. Puisque le «temps» n'est importé que pour le traitement du poids, rien ne peut être dit quand on dit qu'il n'est pas réellement nécessaire.
import time
import subprocess
print("Current version of pwsh ... ", end='', flush=True)
time.sleep(0.5)
cpl_pwsh = subprocess.run("pwsh -v",capture_output=True)
v_local = parseVersion(cpl_pwsh.stdout.strip())
print(v_local.formatVersion())
Je n'ai pas fait beaucoup d'élaboration.
import ssl, urllib.request
import json
directory_download = R"path\to\directoryof\installer"
if(v_local < v_latest):
print("Later version available.")
print("Please wait...")
aslist = content['assets']
vlatest = attr_latest.getstr_version()
targetname = '-'.join(["PowerShell",vlatest,"win-x64.msi"])
targeturl = None
for asset in aslist:
if(asset['name'] == targetname):
targeturl = asset['browser_download_url']
break
if targeturl:
try:
print("Downloading installer... ",end='',flush=True)
with urllib.request.urlopen(targeturl,context=context) as res:
dat_pack = res.read()
except urllib.error.HTTPError as err:
print(err.code)
except urllib.error.URLError as err:
print(err.reason)
except json.JSONDecodeError as err:
print(err.msg)
try:
path = '\\'.join([directory_download,targetname])
f_installer = open(path,mode='xb')
f_installer.write(dat_pack)
f_installer.close()
except OSError:
print()
raise
except:
print("Unexpected error occurred.")
raise
subprocess.run(path, stderr=subprocess.STDOUT)
else:
raise Exception("lost download url.")
elif(attr_pwsh.version == attr_latest.version):
print("Your pwsh is latest version.\n")
else:
raise Exception("unidentified exception occurred.")
J'ai ajouté un peu de décoration au début et à la fin, donc quand je le lance, ça ressemble à ça. Bien sûr, c'est la dernière version maintenant, donc je vais la laisser jusqu'à la prochaine mise à jour de pwsh pour tester si la mise à jour peut être effectuée correctement.
================================================
Powershell updater version 0.5.201114
by Lat.S (@merliborn)
Latest version of pwsh ... 7.1.0
Current version of pwsh ... 7.1.0
Your pwsh is latest version.
Push return key to quit:
================================================
Je suis content de pouvoir faire tout ce que je voulais faire depuis longtemps, comme utiliser HTTPS, accéder à l'API pour obtenir des informations et créer quelque chose avec python en premier lieu. Le fait que les annotations de type puissent être écrites dans des expressions à cause des décorations était très choquant pour moi en tant que personne qui vit habituellement une vie typée.
Recommended Posts