So the trick here is to use the file://
URL protocol:
First set up a paclet server in the standard way (see also my brief explanation here)
Next we need to set up our file://
URL properly. For example, I keep the local version of my paclet server here, before pushing it to the cloud:
serverDir =
FileNameJoin@
{
$UserBaseDirectory,
"ApplicationData",
"WebSites",
"PacletServer"
};
To create an appropriate file URL we'll need to tweak the output of URLBuild
like so:
serverURL =
StringReplace["file:" -> "file://"]@
URLBuild@
<|
"Scheme" -> "file",
"Path" ->
FileNameSplit[serverDir]
|>;
Then we can install a paclet like so:
PacletInstall[
"SiteBuilder",
"Site" -> serverURL
]
PacletManager`Paclet[
"Name" -> "SiteBuilder", "Version" -> "1.0.8",
"Creator" -> "[email protected]",
"Description" -> "Implements a configuration scheme for building and \
deploying sites a la pelican.\n Forked from BTools.",
"Extensions" -> {{
"Kernel", "Root" -> ".", "Context" -> {"SiteBuilder`"}}},
"Tags" -> {"web"}, "Categories" -> {"Services"},
"Location" -> "~/Library/Mathematica/Paclets/Repository/\
SiteBuilder-1.0.8"]
Note for pre-11.2:
Owing to a bug in the PacletManager that seems to have been fixed in 11.2, the file:// protocol was not appropriately supported. If you're using 11.1 or below you'll need to patch the PacletInstall
functions like so:
If[$VersionNumber < 11.2,
PacletManager`Services`Private`finishPacletSiteUpdate[
{
PacletManager`Private`siteURL_,
PacletManager`Private`file_,
PacletManager`Private`interactive_,
PacletManager`Private`async_,
0}
] :=
PacletManager`Services`Private`finishPacletSiteUpdate[
{
PacletManager`Private`siteURL,
PacletManager`Private`file,
PacletManager`Private`interactive,
PacletManager`Private`async,
200}
];
PacletManager`Package`getTaskData[task_] :=
Block[{PacletManager`Private`$override = True},
Replace[PacletManager`Package`getTaskData[task],
{
PacletManager`Private`a_,
PacletManager`Private`b_,
PacletManager`Private`c_,
PacletManager`Private`d_,
PacletManager`Private`e_, 0,
PacletManager`Private`rest__} :>
{
PacletManager`Private`a,
PacletManager`Private`b,
PacletManager`Private`c,
PacletManager`Private`d,
PacletManager`Private`e,
200,
PacletManager`Private`rest}]
] /; ! TrueQ[PacletManager`Private`$override]
]
Helper Function:
We can wrap all of this up into a convenient local-install function:
localPacletPull[pacletName_, dir : (_String | _File)?DirectoryQ] :=
With[{
serverURL =
StringReplace["file:" -> "file://"]@
URLBuild@
<|
"Scheme" -> "file",
"Path" ->
FileNameSplit[dir]
|>
},
If[$VersionNumber < 11.2,
PacletManager`Services`Private`finishPacletSiteUpdate[
{
PacletManager`Private`siteURL_,
PacletManager`Private`file_,
PacletManager`Private`interactive_,
PacletManager`Private`async_,
0}
] :=
PacletManager`Services`Private`finishPacletSiteUpdate[
{
PacletManager`Private`siteURL,
PacletManager`Private`file,
PacletManager`Private`interactive,
PacletManager`Private`async,
200}
];
PacletManager`Package`getTaskData[task_] :=
Block[{PacletManager`Private`$override = True},
Replace[PacletManager`Package`getTaskData[task],
{
PacletManager`Private`a_,
PacletManager`Private`b_,
PacletManager`Private`c_,
PacletManager`Private`d_,
PacletManager`Private`e_, 0,
PacletManager`Private`rest__} :>
{
PacletManager`Private`a,
PacletManager`Private`b,
PacletManager`Private`c,
PacletManager`Private`d,
PacletManager`Private`e, 200,
PacletManager`Private`rest}]
] /; ! TrueQ[PacletManager`Private`$override]
];
If[Length@PacletFind[pacletName] > 0,
PacletUpdate[pacletName,
"Site" -> serverURL
],
PacletInstall[pacletName,
"Site" -> serverURL
]
]
];
So, e.g., if I know my paclet server is at ~/Dropbox/PacletServer, I can do:
localPacletPull[paclet, "~/Dropbox/PacletServer"]
And it will install / update that paclet from my DropBox server