Update installed Plugins via script/cmd



  • Why is everybody so in love with this type of method of updating? Sounds like an awesome way to break a bunch of functionality with buggy new things…



  • From what I’ve heard, I.T. departments like automating and scripting updates as much as possible, because they have to deal with so many machines. For mere mortals like me, who only ever has to take care of his home pc and try to prevent ever needing to deal with I.T. on his work pc, it seems overkill… But I think if your job involves updating dozens of applications on hundreds of PCs, being able to automate is useful. :-)



  • I understand that part. The part that I don’t understand is the “if it ain’t broke why fix it part”. Notepad++ has demonstrated time and time again that it is not tested well (IMO, but if it is not your opinion, do you remember Plugins Admin, encoding detection, etc etc), thus for people that don’t have time to “play” with it (I guess the peoples that do is us !), all the updating is just going to lead to frustration. You heard it here first. :)



  • Well, I would hope that anyone in an IT department who didn’t want users to have permission to upgrade their own plugins would vet the various plugins before forcing the upgrade on users. That may be optimistic on my part. :-)



  • Thanks for your fast answers and the input.
    @PeterJones I’ll give the feature request a try. :)
    @Alan-Kilborn I really get and support your “never change a running system” point. Thing is that our users always get notifications for newer versions being available and they also want the newest version. I’d also say that (in general [and I’m not really familiar with how much this applies for np++ or its plugins]) most of the time updates are more about fixing security issues than fixing broken parts of the program.



  • @Eddga

    i currently deploy plugin updates like this (notepad++ v7.6.3, 32 bit, default installation, semi automatic deployment):

    first i update all plugins manually on one admin machine.
    then i make a copy of %ProgramFiles(x86)%\Notepad++\plugins\ to a network drive, reachable by all user pc’s.
    (it will be %ProgramFiles%\Notepad++\plugins\ if you use the x64 version on all systems)
    then i deploy them with a batch script that is started on all machines at startup, via scheduled tasks (schtasks), using either the system account or an admin account, in order to have permissions to write to %ProgramFiles(x86%).
    this startup batch script just copies all files from \\Servername_or_ip\Updates\Notepad++\plugins\ to %ProgramFiles%\Notepad++\plugins\ on each machine.

    please report back, if you can use, refine or simplify this way of deploying plugins.



  • @Meta-Chuh thanks for the answer!
    Actually that’s the exact way I’m working this around at the moment. Only that we are using DeskCenter as deployment software and I’m doing it via this one. But that doesn’t seem to be a really convenient way to me. I mean, sure it works and may be the way to do it for now, but it still takes some additional minutes of my time and also needs me to check for updates myself every now and then. I guess I’ll do the feature request and meanwhile use the existing workaround.



  • @Eddga

    but it still takes some additional minutes of my time and also needs me to check for updates myself every now and then

    yes, same for me, that’s why i wrote semi automatic.

    to make it automatic, we could parse the json live plugin lists from here:
    notepad++ latest plugin list (x86): https://raw.githubusercontent.com/notepad-plus-plus/nppPluginList/master/src/pl.x86.json
    notepad++ latest plugin list (x64): https://raw.githubusercontent.com/notepad-plus-plus/nppPluginList/master/src/pl.x64.json

    automatically downloading and extracting the needed plugins from the given urls inside the list into our deployment network drive.
    then deploy them at the startup scripts with e.g. cwrsync or robocopy, in order that they will only be copied if anything has changed.

    unfortunately i didn’t write this for myself yet.
    i wanted to implement that already, but i didn’t have time (and/or drive) to do so for now.



  • @Meta-Chuh

    Just in case that this might be helpful to you (or whomever),
    here a python script which does more or less what you want it to do.
    Changes needs to be done in main function.
    For the first time run it downloads everything and creates the two needed local json files,
    which will be used to compare with the web json files in future runs.

    import urllib
    import urllib2
    import os
    from zipfile import ZipFile
    import json
    
    empty_dict = {u'arch': u'32',
                  u'name': u'npp-pluginList',
                  u'npp-plugins': [   {   u'author': u'',
                                          u'description': u'',
                                          u'display-name': u'',
                                          u'folder-name': u'',
                                          u'homepage': u'',
                                          u'id': u'',
                                          u'repository': u'',
                                          u'version': u''
                                      },
                                  ],
                  u'version': u'0'}
    
    
    download_directory = None
    extract_to_directory = None
    
    
    def extract_plugin(_file, _folder):
        try:
            extract_to = os.path.join(extract_to_directory, _folder)
            with ZipFile(_file, 'r') as zip:
                zip.extractall(extract_to)
        except Exception as e:
            print('-->> extracting {} failed with error {}'.format(_file, e))
    
    
    def plugin_downloaded(repo):
        success = False
        saved_file = None
        try:
            print('going to download {}'.format(repo))
            _,_,_file = repo.rpartition('/')
            if not _file.endswith('.zip'):
                    _file += '.zip'
            _file = urllib.url2pathname(_file)
    
            if not os.path.isdir(download_directory):
                os.makedirs(download_directory)
    
            saved_file = os.path.join(download_directory, _file)
            f = urllib2.urlopen(repo, timeout=3)
            data = f.read()
            with open(saved_file, 'wb') as f:
                f.write(data)
    
            success = True
        except Exception as e:
            print('-->> downloading {} failed with error {}'.format(repo, e))
        return success, saved_file
    
    
    def check_for_updated_plugin(new_plugin_list, local_plugin_list):
        lookup = {plugin['id']:plugin for plugin in local_plugin_list}
        for new_plugin in new_plugin_list:
            if new_plugin['id'] in lookup:
                if new_plugin['version'] != lookup[new_plugin['id']]['version']:
                    successful, _file = plugin_downloaded(new_plugin['repository'])
                    if successful:
                        extract_plugin(_file, new_plugin['folder-name'])
            else:
                successful, _file = plugin_downloaded(new_plugin['repository'])
                if successful:
                    extract_plugin(_file, new_plugin['folder-name'])
    
    
    def main():
        global download_directory
        global extract_to_directory
    
        for arch in ['x86','x64']:
            download_directory = r'D:\npp\download_plugins\{}\download'.format(arch)
            extract_to_directory = r'D:\npp\download_plugins\{}\extractto'.format(arch)
            local_plugin_json_file = r'D:\npp\download_plugins\plugin{}.json'.format(arch)
            web_plugin_json_url = r'https://raw.githubusercontent.com/notepad-plus-plus/nppPluginList/master/src/pl.{}.json'.format(arch)
    
            print('\n{}\n\nChecking plugins for architecture:{}'.format('#'*80, arch))
            try:
                data = urllib.urlopen(web_plugin_json_url)
                downloaded_plugin_json = json.load(data)
    
                if not os.path.exists(local_plugin_json_file):
                    __local_plugin_json = empty_dict
                else:
                    with open(local_plugin_json_file, 'r') as f:
                        __local_plugin_json = json.load(f)
    
                if __local_plugin_json['version'] != downloaded_plugin_json['version']:
                    check_for_updated_plugin(downloaded_plugin_json['npp-plugins'], __local_plugin_json['npp-plugins'])
                    with open(local_plugin_json_file, 'w') as f:
                        json.dump(downloaded_plugin_json, f)
                else:
                    print('No updates available')
    
            except Exception as e:
                print('-->> Something went wrong for arch {}: {}'.format(arch, e))
    
            finally:
               print('\n' + '#'*80)
    
        print('finished')
    
    
    main()
    
    


  • @Ekopalypse

    this is soooo nice !!! 🙏😃👍
    i somehow counted on that i can count on you 😉

    i have just tested your script within the PythonScript plugin and it works like a charm.
    this is so awesome, it simply deserves more explanation, so i will promote it a bit with a little …


    plugins auto downloader manual:
    (© Ekopalypse 2019)

    features:

    • all plugins will be automatically downloaded for both x86 and x64 architectures.

    • all plugins will be automatically extracted to a freely configurable plugins location.

    • all plugin zips are kept at an extra location. the admin can freely choose whether to deploy from a set of automatically downloaded zip files, or copying the required, automatically extracted plugins directly.

    • plugins that have already been downloaded, will be skipped and not downloaded again.
      an admin can start this script as many times as he wants, without producing elevated internet traffic.
      (if all plugins are up to date, it will only take appx 2 seconds for the comparison at a re-run.)


    setup and usage:

    • the desired paths, where the plugins should be downloaded to, have to be set at the variables
      download_directory, extract_to_directory and local_plugin_json_file within the main() section of plugins auto updater.py.
      important note: the paths for download_directory and extract_to_directory begin after = r' and end before {},
      except local_plugin_json_file where the path begins after local_plugin_json_file = r' and ends before plugin{}.json as the last part is the json filename instead of a folder, as seen at the demo path setup below.

    • demo path setup:
      in the path customisation example below, i’ve used the destination path H:\Downloads\plugin\:

    download_directory = r'H:\Downloads\plugins\{}\download'.format(arch)
    extract_to_directory = r'H:\Downloads\plugins\{}\extractto'.format(arch)
    local_plugin_json_file = r'H:\Downloads\plugins\plugin{}.json'.format(arch)
    
    • to run this script, execute it in any python environment, either from the command line, a batch script, scheduled task, or the notepad++ pythonscript plugin, which can also be set to execute on every notepad++ startup.

    notes:

    • all x86 plugin zip files can be found at YourCustomPath\plugins\x86\download\ and all x64 plugin zips at YourCustomPath\plugins\x64\download\

    • all extracted x86 plugins will be at YourCustomPath\plugins\x86\extractto\ and all extracted x64 plugins at YourCustomPath\plugins\x64\extractto\

    important note: all re-publications of this script, distribution outside the notepad++ community, integration to any public repo not owned or maintained by the author, or posting this script at a different location, require the consent of the scripts author @Ekopalypse before doing so.



  • @Ekopalypse

    ps: nur um mal auf nummer sicher zu gehen: in diesem fall ist “nett” nicht der kleine bruder von scheisse, sondern einfach geil, saugeil sogar 😉


    ps: just to be on the safe side: in this case “nice” is not the little brother of s… but simply cool, even as cool as a pig 😉

    i know, i know, it doesn’t make much sense when translated to english, but afaik “nice” in german is sometimes kind of an insult.
    for example if a girl says: “you are such a nice guy”, she usually means that he’s the little mini-me of a dude she wouldn’t even date if he would be the last man on earth … but even more boring than that 😂

    and saugeil is as untranslatable as affengeil, which is best described by >>> this 80’s song <<<.



  • @Meta-Chuh
    First, thank you for your awesome usage guide.
    Works exactly like you mentioned. :-D thumbs up.
    @all
    And of course, everyone can use it like they want.
    It is free to use like the free in free beer.
    Like with every software, don’t rely on it - check from time to time
    whether it still works as the script doesn’t do much error checking.



  • gagagaggei… :-D