Packaging is the final step of getting a solution that has been developed into a format that can be understood and installed by the Add-on Manager.
As a reminder, for our example Add-on, the files that need to be packaged are the following:
CODE
.
├── addon.json
├── additional_content
│ └── previewImage.png
├── data-lab-functions
│ ├── API.ipynb
│ └── requirements.txt
├── frontend
│ └── com.seeq.addon.example.frontend.plugin
├── test_tool
│ ├── TestTool.ipynb
│ └── requirements.txt
└── udf
└── formula_package.json
This can be done manually, but it is better to use tooling to make it automated and repeatable. The approach you take is up to you.
One approach is to copy only the required folders and files into an image folder, then zip the contents of the folder, and name the resulting file following the Add-on naming recommendations (e.g. com.seeq.addon.example-1.0.0.addon
).
Another option is to read the element folders directly and copy the contents into a zip file. The following is an example script using that approach to create a valid, well-named Add-on in a ./bin
subfolder from the folder structure displayed above:
Example script for packaging the example Add-on: build.py
PY
import os
import zipfile
import glob
import json
ADD_ON_DEFINITION_FILE = 'addon.json'
ELEMENT_FOLDERS = ['test_tool', 'frontend', 'data-lab-functions', 'udf']
OUTPUT_FOLDER = './bin'
def get_add_on_configuration() -> dict:
with open(ADD_ON_DEFINITION_FILE) as json_file:
try:
return json.load(json_file)
except:
raise Exception(f"Unable to read {ADD_ON_DEFINITION_FILE} definition file")
def addon_filename() -> str:
add_on_configuration = get_add_on_configuration()
required_parameters = ["identifier", "version"]
file_name_parameters = {}
for p in required_parameters:
file_name_parameters[p] = add_on_configuration.get(p, False)
if all(file_name_parameters.values()):
return f'{file_name_parameters["identifier"]}-{file_name_parameters["version"]}.addon'
else:
not_found = ", ".join([k for k, v in file_name_parameters.items() if not v])
raise Exception(f"The following parameterse were not found: {not_found}")
def build(include_metadata=False):
print("Building...")
if not os.path.exists(OUTPUT_FOLDER):
os.makedirs(OUTPUT_FOLDER)
with zipfile.ZipFile(f"{OUTPUT_FOLDER}/{addon_filename()}", "w") as zip:
zip.write(ADD_ON_DEFINITION_FILE)
for folder in ELEMENT_FOLDERS:
for file in glob.glob(f"./{folder}/**", recursive=True):
print(f"Adding {file}")
zip.write(file)
for image_path in get_add_on_configuration()["previews"]:
print(f"Adding ./{image_path}")
zip.write(f"./{image_path}")
print("Building .addonmeta")
with zipfile.ZipFile(f"{OUTPUT_FOLDER}/{addon_filename()}meta", "w") as zip:
# Metadata only requires addon.json and the preview images
zip.write(ADD_ON_DEFINITION_FILE)
for image_path in get_add_on_configuration()["previews"]:
print(f"Adding ./{image_path}")
zip.write(f"./{image_path}")
print("Done")
if __name__ == "__main__":
build()
This script will read the addon.json
configuration file and package all the elements that are defined in the ELEMENT_FOLDERS
variable. It zip the content and output the <identifier>-<version>.addon
and <identifier>-<version>.addonmeta
files to the OUTPUT_FOLDER
. Where <identifier>
and <version>
are defined in the addon.json
file.