Virtual Packages in Universal Package Feeds
A Virtual Package is a special type of Universal Package that contains no content (i.e. application files, components, metadata files, etc.). It's simply a package manifest file (i.e. upack.json
) with instructions on where to download the content.
When you upload a Virtual Package to a feed, it will appear and function as an ordinary universal package in most cases. When a user or client downloads the package from the feed, ProGet will transparently assemble the package file.
Creating Virtual Packages
A Virtual Package is JSON-based Universal Package Manifest file with an additional content
property that describes the package's content (i.e. what files will be placed in the packages/
folder in the package file).
For example, the following Virtual Package simply combines two other universal packages into one:
# HDARS.Combined-1.3.9.vpack
{
"name": "HDARS.Combined",
"version": "1.3.9",
"contents": [ "HDARS.Web:1.3.9", "HDARS.API:1.3.9" ]
}
ProGet will assemble the HDARS.Combined
package file by taking the contents from the referenced packages (i.e. files under package/
in the package file) and adding them to the packages/
folder in the new package file.
HDARS.Web-1.3.9 | HDARS.API-1.3.9 | HDARS.Combined-1.3.9v |
---|---|---|
upack.json package/index.htm package/logo.gif |
upack.json package/cgi-bin/api.py |
upack.json package/index.htm package/logo.gif package/cgi-bin/api.py |
Note that the manifest files (upack.json
) from the referenced packages are not used. The new manifest file (upack.json
) will contain all of the same properties as the virtual package file, except the contents
property:
# upack.json
{
"name": "HDARS.Combined",
"version": "1.3.9"
}
Using Virtual Paths
By default, ProGet will assemble referenced resources into the new package's content root (i.e. packages/
). You can specify a subfolder by using a Virtual Content Object with a targetPath
property.
For example, consider this virtual package that's assembled from 4 packages:
# ErpProduct.Initech-2.2.1.vpack
{
"name": "ErpProduct.Initech",
"version": "2.2.1",
"contents": [
"ErpProduct.Core:2.2.1",
{
"source": "Plugins.Initech:2.0.1",
"targetPath": "custom/plugin"
},
{
"source": "Plugins.SalesPipeline:2.1.0",
"targetPath": "custom/plugin"
},
{
"source": "Plugins.Workflows:2.1.0",
"targetPath": "custom/plugin"
}
]
}
The ErpProduct.Core:2.2.1
package is specified as a string, but the other three packages are specified as objects with a source
and targetPath
. As such, those packages' contents will be written to the package/{targetPath}/
folder instead of package/
.
Assuming that each of those packages had a single file, the assembled package might look like this:
package/erp.core.exe
package/custom/plugins/initech.dll
package/custom/plugins/salespipeline.dll
package/custom/plugins/workflows.dll
upack.json
Using Files and Assets
In addition to packages, content from an asset directory or files from a url.
{
"group": "initrode/vendors/abl",
"name": "ABLast.AstDist",
"version": "2.2.1",
"contents": [
{
"virtualPath": "vendors/common/ast",
"source": "initrode/vendors/abl/ABlast:2.2.1:ab60bf74fc8147ca41bd53bdb1defc3aae35bc91"
},
{
"virtualPath": "common/logo/logo.png",
"source": {
"url": "http://proget/endpoints/customer-assets/content/ast-logo.png"
}
},
{
"virtualPath": "common/logo/logo.png",
"source": {
"feed": "customer-assets",
"asset": "logos/ast-logo.png"
}
}
]
}
Metacontents
By default, ProGet will assemble package contents to the packages/
folder in the new package file. However, if you specify items in the metaContents
folder, they will instead be written to the root folder in the package file.
Usecases for Virtual Packages
There are two main use cases for virtual packages:
Usecase: Bundling Multiple Packages
There are several cases where you may want to bundle a number of packages into a single, logical package.
Let us say you have created an application that has been customized for six different clients by overwriting logos, JavaScript files, or other resources within the application. The "base application" is completely useless on its own, except for testing and quality assurance purposes, and the "resources" are even more useless. They must be combined with a specific version of the "base application" to be tested at all.
This can be easily solved with a virtual package that "bundles" a specific version of the base application (CrmAppBase
) and the client customization (InitechApp
) into a single, logical package (Initech.crm
).
When you deliver the package, it will be exactly what is needed. However, you can easily see from the metadata which versions/components were used to assemble the package.
Usecase: Logical Packaging of External Files
Another way to use Virtual Packages is to reference known external files.
For example, if your package contains large, immutable, multi-gigabyte assets (such as movies or map files), maintaining and transferring multiple versions of these packages is slow and requires a lot of disk resources. Instead, you can use file references to logically pack these resources into one package.
You can also bundle these files together with your regular package contents by bundling them.
Assembling Virtual Package Contents
When assembling a virtual package, ProGet uses the following logic:
The
contents
array is enumerated, and each resource is assessed as follows:- If it's a file resource: the contents are downloaded, verified (if a hash is specified), and written to the target virtual path. A directory is created if it doesn't exist, and a file is ignored if it already exists.
- If it's a package resource: the current package feed is queried for the specified package (including hash). The contents are then extracted to the target virtual path, ignoring any existing files.
The
metaContents
array is enumerated; each resource is assessed as follows:- If the virtualPath is
upack.json
or within the packages directory: the operation fails - If it's a file resource: the contents are downloaded, verified (if a hash is specified), and written to the target virtual path. A directory is created if it doesn't exist, and a file is ignored if it already exists.
- If it's a package resource: the current feed is queried for the specified package (including hash). The contents are then extracted to the target virtual path, ignoring any existing files.
- If the virtualPath is
If any error occurs during assembly, then the entire operation fails.
Depending on the size of the referenced files and packages, package assembly may be resource-intensive and time-consuming. However, ProGet will save assembled packages to the feed's package store using a .constructed
extension and will use this file for subsequent requests.
Specifications & Data Objects
Additional Manifest File Properties
Property | Format |
---|---|
contents R |
An array of at least one item, containing any of the following: • string - a package identification string • string - an absolute URL to an HTTP or HTTPS file resource • object - virtual content object A package identification string is a 3- or 4-part string, formatted as follows: • «group»/«package-name»:«version» • «group»/«package-name»:«version»:«sha1-hash» |
metaContents |
same as contents property |
Virtual Content Object
This object describes a remote file or folder within either the root of the content (package/
) or package root (/
) folder, depending on whether it's within the contents
or metaContents
property.
Property | Format |
---|---|
type | A string of either virtualDirectory or virtualFile that describes the content to be added. The default value is virtualDirectory , and is used only in the case of missing property. All other values (including null or empty) are invalid. |
virtualPath | A string of a path to a file or folder within either the root of the content (package/ ) or package root (/ ) folder, depending on whether the object is within the contents or metaContents property.A missing property, or null, empty, or "/" string means the root within that path. When used in metaContent, a value of upack.json is invalid, as the manifest can never be virtualized. A value that begins with package/ is also invalid for metaContent. |
source | One of the following values:
|
Package Source Object
This object describes where either a virtualFile
or virtualFolder
(as specified by the parent object) can be found.
Property | Format |
---|---|
group |
A string of the referenced package group. When not specified, the empty group is used. |
name R |
A string of the referenced package name. |
version R |
A string of the referenced package version. |
hash |
A SHA-1 hash string of file contents |
packagePath |
A string containing the path within the package file (i.e. under the / directory of the archive file) the content can be found. A missing property or null value will default to package/ . |
File Source Object
This object describes where a virtualFile
can be found.
Property | Format |
---|---|
url R |
A string of an absolute HTTP/HTTPS resource where file contents can be found |
hash |
A SHA-1 hash string of file contents |