16
$\begingroup$

It seems that some Wolfram-provided packages are smart enough to automatically check with the Wolfram servers, notice when a newer version is available, and download it, so that the user always has the latest version. I'd like to implement this with packages that I've developed and distributed. Is this possible? If so, how?

$\endgroup$
  • 5
    $\begingroup$ I did not know that WRI did this. But this seems to me to be a bad idea. I'd rather get a notification that there is a new package version, and let the user decide when and if they want to update. You can make the update very easy, sure. But I do not want external software to update anything on my PC without me knowing about it. $\endgroup$ – Nasser Dec 3 '15 at 3:51
  • $\begingroup$ By "package" do you mean paclet? Wolfram auto-updates paclet data, but I'm not familiar with packages being updated outside of full Mathematica releases. $\endgroup$ – ZachB Dec 3 '15 at 7:28
  • 3
    $\begingroup$ Some of the paclets also contain functions. There is a discussion on community where I reported a bug in one of the AstronomicalData functions. This bug was corrected in a paclet update. AFAIK there is no published mechanism to do this with your own packages. $\endgroup$ – Sjoerd C. de Vries Dec 3 '15 at 12:00
5
$\begingroup$

You can home-brew something like this...

  1. Host your package on a publicly-accessible server. You could use Amazon S3 or Google Cloud Storage (GCS) (they both have free or trial tiers), GitHub, or your own server. I've never tried it, but Wolfram's new cloud stuff could potentially work too.
  2. For S3 or GCS, add a metadata tag, such as "version", to your hosted package that states the version (see links in note 2 below). For GitHub, tag your release.
  3. Add a new file to your package that serves as a package loader/maintainer. This will be the file that users Get (e.g. `<<"MyPackage.wl"). The contents would be something like this:

    fileToLoad = FileNameJoin[{DirectoryName[System`Private`$InputFileName], "myActualPackage.wl"}];
    
    (* Read the first line of the package, which must be like 'version=1.0', with no semicolon. See note 1. *)
    Block[{f = OpenRead[fileToLoad]}, currentVersion = Read[f]; Close[f]];
    
    (* Use HEAD Object to get the latest version from the metadata. See note 2. *)
    Block[{headers},
        headers = URLFetch["url.for/your/package", "Headers", "Method"->"HEAD"];
        latestVersion = ToExpression[First@Cases[headers, {"x-amz-meta-version", v_} :> v]];
    ]
    
    (* Update if necessary. See note 3. *)
    If[latestVersion > currentVersion,
        (* Consider prompting the user if they want to update. *)
        PrintTemporary[Panel["Updating to version " <> ToString[latestVersion] <> "..."]];
        URLSave["url.for/your/package", fileToLoad];
    ]
    
    (* Finally load your actual package *)
    Get[fileToLoad]
    

Note 1: There are a few ways to store your current version. Perhaps a better way, especially if your package is already more than one file, is to use a new file that stores only the version.

Note 2: See the S3 docs for metadata, S3 docs for HEAD Object, Google Cloud Storage docs for metadata and GCS docs for HEAD Object. The metadata is encoded in the headers as "x-amz-meta-" for S3 and "x-goog-meta-" for Google, where * is your metadata key. Using HEAD fetches just the metadata without downloading the whole file. (You could also work out something a bit fancier using ETags...) If you decide to use GitHub, use the GitHub API to fetch the latest release.

Note 3: You can tweak this, e.g. if you want separate folders for new versions or if your package is multiple files distributed as a ZIP that needs to be unpacked. If the file is a ZIP, I would just use URLSave["url.for/your/package"] to save it to a temp directory, then use ExtractArchive, CopyDirectory and DeleteDirectory to move it into place.

Edit 11-Dec-2015: Changed NotebookDirectory[] on first line to DirectoryName[System`Private `$InputFileName].

| improve this answer | |
$\endgroup$
  • $\begingroup$ Interesting. Disappointing not to have the functionality ready-made by Wolfram, but this looks doable. I'm serving from Apache running on my account with a hosting provider, so I'll have to figure out how to serve up the metadata header through that. (And I hadn't known about PrintTemporary, either. Thanks for that, too!) $\endgroup$ – ibeatty Dec 8 '15 at 2:41
  • 1
    $\begingroup$ Check out mod_headers for apache... Good luck! $\endgroup$ – ZachB Dec 8 '15 at 5:44
  • $\begingroup$ On a related note: Does anyone know of any documentation for the paclet system? It's intriguing, but I don't even know enough about it to know whether it might have functionality I could make use of. $\endgroup$ – ibeatty Dec 10 '15 at 21:08

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.