-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Improve package signing plugin integration #19345
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop2
Are you sure you want to change the base?
Changes from 16 commits
d5c13c4
eac6b76
05d46b5
861ddca
7ac7ba9
8b599ac
9c8ee75
fbff444
8fa65b1
ce0d59a
e539f0a
2f2037b
2b75511
faea045
97963b7
03d0da6
bb99d7b
f397ae5
0b1e43c
bdb8e25
d76b0df
4645b3d
6ab0545
1ffd6f3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -17,6 +17,9 @@ | |
| from conan.errors import ConanException | ||
| from conan.api.model import PkgReference | ||
| from conan.api.model import RecipeReference | ||
| from conan.internal.api.uploader import PackagePreparator | ||
| from conan.internal.conan_app import ConanApp | ||
| from conan.internal.rest.pkg_sign import PkgSignaturesPlugin | ||
| from conan.internal.util.dates import revision_timestamp_now | ||
| from conan.internal.util.files import rmdir, mkdir, remove, save | ||
|
|
||
|
|
@@ -77,6 +80,69 @@ def check_integrity(self, package_list): | |
| checker = IntegrityChecker(cache) | ||
| checker.check(package_list) | ||
|
|
||
| def sign(self, package_list): | ||
| """Sign packages with the package signing plugin""" | ||
| cache = PkgCache(self._conan_api.cache_folder, self._api_helpers.global_conf) | ||
| pkg_signer = PkgSignaturesPlugin(cache, self._conan_api.home_folder) | ||
| if not pkg_signer.is_configured: | ||
| raise ConanException( | ||
| "The package sign plugin is not configured. For more information on how to " | ||
| "configure it, please read the documentation at " | ||
| "https://docs.conan.io/2/reference/extensions/package_signing.html.") | ||
| app = ConanApp(self._conan_api) | ||
| preparator = PackagePreparator(app, self._api_helpers.global_conf) | ||
| preparator.prepare(package_list, [], force=True) | ||
|
|
||
| for rref, packages in package_list.items(): | ||
| recipe_bundle = package_list.recipe_dict(rref) | ||
| if recipe_bundle: | ||
danimtb marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| rref_folder = cache.recipe_layout(rref).download_export() | ||
| try: | ||
| pkg_signer.sign_pkg(rref, recipe_bundle.get("files", {}), rref_folder) | ||
| except Exception as e: | ||
| recipe_bundle["error"] = e | ||
danimtb marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
danimtb marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| for pref in packages: | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is it possible to have
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think both should be signed, as packages cannot be uploaded without their recipe, and the recipe is the proxy to packages, so it makes sense to have both signed |
||
| pkg_bundle = package_list.package_dict(pref) | ||
| if pkg_bundle: | ||
| pref_folder = cache.pkg_layout(pref).download_package() | ||
| try: | ||
| pkg_signer.sign_pkg(pref, pkg_bundle.get("files", {}), pref_folder) | ||
| except Exception as e: | ||
| pkg_bundle["error"] = e | ||
| return package_list | ||
|
|
||
| def verify(self, package_list): | ||
| """Verify packages with the package signing plugin""" | ||
| cache = PkgCache(self._conan_api.cache_folder, self._api_helpers.global_conf) | ||
| pkg_signer = PkgSignaturesPlugin(cache, self._conan_api.home_folder) | ||
| if not pkg_signer.is_configured: | ||
danimtb marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| raise ConanException( | ||
| "The package sign plugin is not configured. For more information on how to" | ||
| "configure it, please read the documentation at " | ||
| "https://docs.conan.io/2/reference/extensions/package_signing.html.") | ||
|
|
||
| for rref, packages in package_list.items(): | ||
| recipe_bundle = package_list.recipe_dict(rref) | ||
| if recipe_bundle: | ||
danimtb marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| rref_folder = cache.recipe_layout(rref).download_export() | ||
| files = {file: os.path.join(rref_folder, file) for file in | ||
| os.listdir(rref_folder) if not file.startswith(METADATA)} | ||
| try: | ||
| pkg_signer.verify(rref, rref_folder, files) | ||
| except Exception as e: | ||
| recipe_bundle["error"] = e | ||
| for pref in packages: | ||
| pkg_bundle = package_list.package_dict(pref) | ||
| if pkg_bundle: | ||
| pref_folder = cache.pkg_layout(pref).download_package() | ||
| files = {file: os.path.join(pref_folder, file) for file in | ||
| os.listdir(pref_folder) if not file.startswith(METADATA)} | ||
| try: | ||
| pkg_signer.verify(pref, pref_folder, files) | ||
| except Exception as e: | ||
| pkg_bundle["error"] = e | ||
danimtb marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| return package_list | ||
|
|
||
| def clean(self, package_list, source=True, build=True, download=True, temp=True, | ||
| backup_sources=False): | ||
| """ | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,64 @@ | ||
| import copy | ||
| import os | ||
| import json | ||
|
|
||
| from conan.internal.util.files import load, sha256sum, save | ||
|
|
||
| # FIXME: Maybe this tools should be placed at conan.api.subapi.cache as they are not recipe tools? | ||
|
|
||
| SIGN_SUMMARY_CONTENT = { | ||
| "provider": None, | ||
| "method": None, | ||
| "files": {} | ||
| } | ||
|
|
||
| SIGN_SUMMARY_FILENAME = "sign-summary.json" | ||
|
|
||
|
|
||
| def get_summary_file_path(signature_folder): | ||
danimtb marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| """ | ||
| Gets the path of the summary file path | ||
| @param signature_folder: Signature folder path | ||
| @return: Path of the summary file | ||
| """ | ||
| return os.path.join(signature_folder, SIGN_SUMMARY_FILENAME) | ||
|
|
||
|
|
||
| def load_summary(signature_folder): | ||
| """ | ||
| Loads the summary file from the signature folder | ||
| @param signature_folder: Signature folder path | ||
| @return: Dictionary object with the content of the summary | ||
| """ | ||
| return json.loads(load(get_summary_file_path(signature_folder))) | ||
|
|
||
|
|
||
| def create_summary_content(artifacts_folder): | ||
| """ | ||
| Creates the summary content as a dictionary for manipulation | ||
| @param artifacts_folder: Artifacts folder path | ||
| @return: Dictionary with the summary content | ||
| """ | ||
| checksums = {} | ||
| for fname in os.listdir(artifacts_folder): | ||
| file_path = os.path.join(artifacts_folder, fname) | ||
| if os.path.isfile(file_path): | ||
| sha256 = sha256sum(file_path) | ||
| checksums[fname] = sha256 | ||
| assert checksums, f"Summary file content cannot be created: No files found in {artifacts_folder}" | ||
| sorted_checksums = dict(sorted(checksums.items())) | ||
| content = copy.deepcopy(SIGN_SUMMARY_CONTENT) | ||
| content["files"] = sorted_checksums | ||
| return content | ||
|
|
||
|
|
||
| def save_summary(signature_folder, content): | ||
| """ | ||
| Saves the content of the summary to the signature folder using SIGN_SUMMARY_FILENAME as the | ||
| file name | ||
| @param signature_folder: Signature folder path | ||
| @param content: Content of the summary file | ||
| """ | ||
| assert content.get("provider") | ||
| assert content.get("method") | ||
| save(get_summary_file_path(signature_folder), json.dumps(content)) | ||
Uh oh!
There was an error while loading. Please reload this page.