# This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, # You can obtain one at http://mozilla.org/MPL/2.0/.
# Utility package for working with moz.yaml files. # # Requires `pyyaml` and `voluptuous` # (both are in-tree under third_party/python)
import errno import os import re
import voluptuous import yaml from voluptuous import (
All,
Boolean,
FqdnUrl, In,
Invalid,
Length,
Match,
Msg,
Required,
Schema,
Unique,
) from yaml.error import MarkedYAMLError
# LICENSE file must exist, except for Rust crates which are exempted # because the license is required to be specified in the Cargo.toml file if require_license_file and"origin"in manifest:
files = [f.lower() for f in os.listdir(vendor_directory)] if ( not ( "license-file"in manifest["origin"] and manifest["origin"]["license-file"].lower() in files
) andnot ( "license"in files or"license.txt"in files or"license.rst"in files or"license.html"in files or"license.md"in files
) andnot ( "vendoring"in manifest and manifest["vendoring"].get("flavor", "regular") == "rust"
)
):
license = manifest["origin"]["license"] if isinstance(license, list):
license = "/".join(license) raise ValueError("Failed to find %s LICENSE file" % license)
# Cannot vendor without an origin. if"vendoring"in manifest and"origin"notin manifest: raise ValueError('"vendoring" requires an "origin"')
# Cannot vendor without a computer-readable revision. if"vendoring"in manifest and"revision"notin manifest["origin"]: raise ValueError( 'If "vendoring" is present, "revision" must be present in "origin"'
)
# The Rust and Individual Flavor type precludes a lot of options # individual-files could, in theory, use several of these, but until we have a use case let's # disallow them so we're not worrying about whether they work. When we need them we can make # sure they do. if ( "vendoring"in manifest and manifest["vendoring"].get("flavor", "regular") != "regular"
): for i in [ "skip-vendoring-steps", "keep", "exclude", "include", "generated",
]: if i in manifest["vendoring"]: raise ValueError("A non-regular flavor of update cannot use '%s'" % i)
if manifest["vendoring"].get("flavor", "regular") == "rust": for i in [ "update-actions",
]: if i in manifest["vendoring"]: raise ValueError("A rust flavor of update cannot use '%s'" % i)
# Ensure that only individual-files flavor uses those options if ( "vendoring"in manifest and manifest["vendoring"].get("flavor", "regular") != "individual-files"
): if ( "individual-files"in manifest["vendoring"] or"individual-files-list"in manifest["vendoring"]
): raise ValueError( "Only individual-files flavor of update can use 'individual-files'"
)
# Ensure that release-artifact is only used with tag tracking if"vendoring"in manifest and"release-artifact"in manifest["vendoring"]: if (
manifest["vendoring"].get("source-hosting") != "github" or manifest["vendoring"].get("tracking", "commit") != "tag"
): raise ValueError( "You can only use release-artifact with tag tracking from Github."
)
# Ensure that the individual-files flavor has all the correct options if ( "vendoring"in manifest and manifest["vendoring"].get("flavor", "regular") == "individual-files"
): # Because the only way we can determine the latest tag is by doing a local clone, # we don't want to do that for individual-files flavors because those flavors are # usually on gigantic repos we don't want to clone for such a simple thing. if manifest["vendoring"].get("tracking", "commit") == "tag": raise ValueError( "You cannot use tag tracking with the individual-files flavor. (Sorry.)"
)
# We need either individual-files or individual-files-list if ( "individual-files"notin manifest["vendoring"] and"individual-files-list"notin manifest["vendoring"]
): raise ValueError( "The individual-files flavor must include either "
+ "'individual-files' or 'individual-files-list'"
) # For whichever we have, make sure we don't have the other and we don't have # options we shouldn't or lack ones we should. if"individual-files"in manifest["vendoring"]: if"individual-files-list"in manifest["vendoring"]: raise ValueError( "individual-files-list is mutually exclusive with individual-files"
) if"individual-files-default-upstream"in manifest["vendoring"]: raise ValueError( "individual-files-default-upstream can only be used with individual-files-list"
) if"individual-files-default-destination"in manifest["vendoring"]: raise ValueError( "individual-files-default-destination can only be used "
+ "with individual-files-list"
) if"individual-files-list"in manifest["vendoring"]: if"individual-files"in manifest["vendoring"]: raise ValueError( "individual-files is mutually exclusive with individual-files-list"
) if"individual-files-default-upstream"notin manifest["vendoring"]: raise ValueError( "individual-files-default-upstream must be used with individual-files-list"
) if"individual-files-default-destination"notin manifest["vendoring"]: raise ValueError( "individual-files-default-destination must be used with individual-files-list"
)
if"updatebot"in manifest: # If there are Updatebot tasks, then certain fields must be present and # defaults need to be set. if"tasks"in manifest["updatebot"]: if"vendoring"notin manifest or"url"notin manifest["vendoring"]: raise ValueError( "If Updatebot tasks are specified, a vendoring url must be included."
)
if"try-preset"in manifest["updatebot"]: for f in ["fuzzy-query", "fuzzy-paths"]: if f in manifest["updatebot"]: raise ValueError( "If 'try-preset' is specified, then %s cannot be" % f
)
# Check for a simple YAML file with open(filename, "r") as f:
has_schema = False for line in f.readlines():
m = RE_SECTION(line) if m: if m.group(1) == "schema":
has_schema = True break ifnot has_schema: raise ValueError("Not simple YAML")
# Do type conversion for the few things that need it. # Everythig is parsed as a string to (a) not cause problems with revisions that # are only numerals and (b) not strip leading zeros from the numbers if we just # converted them to string def _schema_1_transform(manifest): if"updatebot"in manifest: if"tasks"in manifest["updatebot"]: for i in range(len(manifest["updatebot"]["tasks"])): if"enabled"in manifest["updatebot"]["tasks"][i]:
val = manifest["updatebot"]["tasks"][i]["enabled"]
manifest["updatebot"]["tasks"][i]["enabled"] = (
val.lower() == "true"or val.lower() == "yes"
) return manifest
class UpdateActions(object): """Voluptuous validator which verifies the update actions(s) are valid."""
def __call__(self, values): for v in values: if"action"notin v: raise Invalid("All file-update entries must specify a valid action") if v["action"] in ["copy-file", "move-file", "move-dir"]: if"from"notin v or"to"notin v or len(v.keys()) != 3: raise Invalid( "%s action must (only) specify 'from' and 'to' keys"
% v["action"]
) elif v["action"] in ["replace-in-file", "replace-in-file-regex"]: if ( "pattern"notin v or"with"notin v or"file"notin v or len(v.keys()) != 4
): raise Invalid( "replace-in-file action must (only) specify "
+ "'pattern', 'with', and 'file' keys"
) elif v["action"] == "delete-path": if"path"notin v or len(v.keys()) != 2: raise Invalid( "delete-path action must (only) specify the 'path' key"
) elif v["action"] == "run-script": if"script"notin v or"cwd"notin v: raise Invalid( "run-script action must specify 'script' and 'cwd' keys"
) if set(v.keys()) - set(["args", "cwd", "script", "action"]) != set(): raise Invalid( "run-script action may only specify 'script', 'cwd', and 'args' keys"
) elif v["action"] == "run-command": if"command"notin v or"cwd"notin v: raise Invalid( "run-command action must specify 'command' and 'cwd' keys"
) if set(v.keys()) - set(["args", "cwd", "command", "action"]) != set(): raise Invalid( "run-command action may only specify 'command', 'cwd', and 'args' keys"
) else: # This check occurs before the validator above, so the above is # redundant but we leave it to be verbose. raise Invalid("Supplied action " + v["action"] + " is invalid.") return values
def __repr__(self): return"UpdateActions"
class UpdatebotTasks(object): """Voluptuous validator which verifies the updatebot task(s) are valid."""
def __call__(self, values):
seenTaskTypes = set() for v in values: if"type"notin v: raise Invalid("All updatebot tasks must specify a valid type")
if v["type"] in seenTaskTypes: raise Invalid("Only one type of each task is currently supported")
seenTaskTypes.add(v["type"])
if v["type"] == "vendoring": for i in ["filter", "branch", "source-extensions"]: if i in v: raise Invalid( "'%s' is only valid for commit-alert task types" % i
) elif v["type"] == "commit-alert": pass else: # This check occurs before the validator above, so the above is # redundant but we leave it to be verbose. raise Invalid("Supplied type " + v["type"] + " is invalid.") return values
def __repr__(self): return"UpdatebotTasks"
class License(object): """Voluptuous validator which verifies the license(s) are valid as per our
allow list."""
def __call__(self, values): if isinstance(values, str):
values = [values] elifnot isinstance(values, list): raise Invalid("Must be string or list") for v in values: if v notin VALID_LICENSES: raise Invalid("Bad License") return values
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.