From e6d201e0b015485b5f673818d01087adfa95bddb Mon Sep 17 00:00:00 2001 From: Charlton Austin Date: Tue, 21 Mar 2017 17:24:11 -0400 Subject: [PATCH] feat(build runner): added in context, dockerfile_location this is a new feature meant to allow people to use any file as a dockerfile and any folder as a context directory --- buildman/component/buildcomponent.py | 7 +- buildtrigger/basehandler.py | 33 ++++++- buildtrigger/githubhandler.py | 9 +- buildtrigger/test/bitbucketmock.py | 4 +- buildtrigger/test/githubmock.py | 4 +- buildtrigger/test/gitlabmock.py | 4 +- buildtrigger/test/test_basehandler.py | 40 ++++++++ buildtrigger/test/test_bitbuckethandler.py | 6 +- buildtrigger/test/test_githubhandler.py | 6 +- buildtrigger/test/test_gitlabhandler.py | 6 +- ...463dfb9fe_back_fill_build_expand_config.py | 85 +++++++++++++++++ data/model/build.py | 6 +- data/model/helpers/build_helper.py | 45 +-------- data/model/helpers/test/test_build_helper.py | 22 +---- endpoints/api/build.py | 8 +- endpoints/api/test/test_trigger.py | 22 +++++ endpoints/api/trigger.py | 69 +++++++++++--- endpoints/building.py | 16 ++++ initdb.py | 2 + .../repo-view/repo-panel-builds.html | 4 +- .../context-path-select.component.html | 33 +++++++ .../context-path-select.component.spec.ts | 87 ++++++++++++++++++ .../context-path-select.component.ts | 46 +++++++++ .../manage-trigger-githost.component.html | 41 ++++++++- .../manage-trigger-githost.component.ts | 24 ++++- static/js/quay.module.ts | 2 + test/data/test.db | Bin 1634304 -> 1634304 bytes test/test_api_usage.py | 7 +- tools/migratebranchregex.py | 4 +- 29 files changed, 531 insertions(+), 111 deletions(-) create mode 100644 data/migrations/versions/a6c463dfb9fe_back_fill_build_expand_config.py create mode 100644 endpoints/api/test/test_trigger.py create mode 100644 static/js/directives/ui/context-path-select/context-path-select.component.html create mode 100644 static/js/directives/ui/context-path-select/context-path-select.component.spec.ts create mode 100644 static/js/directives/ui/context-path-select/context-path-select.component.ts diff --git a/buildman/component/buildcomponent.py b/buildman/component/buildcomponent.py index 16887d439..31b9b70bb 100644 --- a/buildman/component/buildcomponent.py +++ b/buildman/component/buildcomponent.py @@ -124,12 +124,13 @@ class BuildComponent(BaseComponent): # username: The username for pulling the base image (if any). # password: The password for pulling the base image (if any). - subdir, dockerfile_name = self.name_and_path(build_config.get('build_subdir')) + # TODO: Charlie Tuesday, March 28, 2017 come back and clean up build_subdir. + dockerfile_path = os.path.relpath(build_config.get('build_subdir'), build_config.get('context')) build_arguments = { 'build_package': build_job.get_build_package_url(self.user_files), - 'sub_directory': subdir, - 'dockerfile_name': dockerfile_name, + 'context': build_config.get('context'), + 'dockerfile_path': dockerfile_path, 'repository': repository_name, 'registry': self.registry_hostname, 'pull_token': build_job.repo_build.access_token.code, diff --git a/buildtrigger/basehandler.py b/buildtrigger/basehandler.py index a929ead8b..f7491e97d 100644 --- a/buildtrigger/basehandler.py +++ b/buildtrigger/basehandler.py @@ -1,3 +1,4 @@ +import os from abc import ABCMeta, abstractmethod from jsonschema import validate from six import add_metaclass @@ -290,7 +291,7 @@ class BuildTriggerHandler(object): def get_dockerfile_path(self): """ Returns the normalized path to the Dockerfile found in the subdirectory in the config. """ - dockerfile_path = self.config.get('subdir') or 'Dockerfile' + dockerfile_path = self.config.get('dockerfile_path') or 'Dockerfile' if dockerfile_path[0] == '/': dockerfile_path = dockerfile_path[1:] return dockerfile_path @@ -303,10 +304,11 @@ class BuildTriggerHandler(object): ref = metadata.get('ref', None) commit_sha = metadata['commit'] default_branch = metadata.get('default_branch', None) - prepared = PreparedBuild(self.trigger) prepared.name_from_sha(commit_sha) - prepared.subdirectory = config.get('subdir', None) + # TODO: Charlie Tuesday, March 28, 2017 come back and clean up subdirectory. + prepared.subdirectory = config.get('dockerfile_path', None) + prepared.context = config.get('context', None) prepared.is_manual = is_manual prepared.metadata = metadata @@ -327,3 +329,28 @@ class BuildTriggerHandler(object): namespaces = list(namespaces_dict.values()) validate(namespaces, NAMESPACES_SCHEMA) return namespaces + + @classmethod + def get_parent_directory_mappings(cls, dockerfile_path, current_paths=None): + """ Returns a map of dockerfile_paths to it's possible contexts. """ + if dockerfile_path == "": + return {} + + if dockerfile_path[0] != os.path.sep: + dockerfile_path = os.path.sep + dockerfile_path + + dockerfile_path = os.path.normpath(dockerfile_path) + all_paths = set() + path, _ = os.path.split(dockerfile_path) + if path == "": + path = os.path.sep + + all_paths.add(path) + for i in range(1, len(path.split(os.path.sep))): + path, _ = os.path.split(path) + all_paths.add(path) + + if current_paths: + return dict({dockerfile_path: list(all_paths)}, **current_paths) + + return {dockerfile_path: list(all_paths)} diff --git a/buildtrigger/githubhandler.py b/buildtrigger/githubhandler.py index d5ec45b73..78ffe580a 100644 --- a/buildtrigger/githubhandler.py +++ b/buildtrigger/githubhandler.py @@ -371,17 +371,22 @@ class GithubBuildTrigger(BuildTriggerHandler): raise RepositoryReadException(message) path = self.get_dockerfile_path() - if not path or not self.filename_is_dockerfile(os.path.basename(path)): + if not path: return None try: file_info = repo.get_file_contents(path) - except GithubException as ghe: + # TypeError is needed because directory inputs cause a TypeError + except (GithubException, TypeError) as ghe: + logger.error("got error from trying to find github file %s" % ghe) return None if file_info is None: return None + if isinstance(file_info, list): + return None + content = file_info.content if file_info.encoding == 'base64': content = base64.b64decode(content) diff --git a/buildtrigger/test/bitbucketmock.py b/buildtrigger/test/bitbucketmock.py index c53f4a57f..83791bbaa 100644 --- a/buildtrigger/test/bitbucketmock.py +++ b/buildtrigger/test/bitbucketmock.py @@ -4,11 +4,11 @@ from mock import Mock from buildtrigger.bitbuckethandler import BitbucketBuildTrigger from util.morecollections import AttrDict -def get_bitbucket_trigger(subdir=''): +def get_bitbucket_trigger(dockerfile_path=''): trigger_obj = AttrDict(dict(auth_token='foobar', id='sometrigger')) trigger = BitbucketBuildTrigger(trigger_obj, { 'build_source': 'foo/bar', - 'subdir': subdir, + 'dockerfile_path': dockerfile_path, 'username': 'knownuser' }) diff --git a/buildtrigger/test/githubmock.py b/buildtrigger/test/githubmock.py index 295956614..f1c71e9c8 100644 --- a/buildtrigger/test/githubmock.py +++ b/buildtrigger/test/githubmock.py @@ -6,9 +6,9 @@ from github import GithubException from buildtrigger.githubhandler import GithubBuildTrigger from util.morecollections import AttrDict -def get_github_trigger(subdir=''): +def get_github_trigger(dockerfile_path=''): trigger_obj = AttrDict(dict(auth_token='foobar', id='sometrigger')) - trigger = GithubBuildTrigger(trigger_obj, {'build_source': 'foo', 'subdir': subdir}) + trigger = GithubBuildTrigger(trigger_obj, {'build_source': 'foo', 'dockerfile_path': dockerfile_path}) trigger._get_client = get_mock_github return trigger diff --git a/buildtrigger/test/gitlabmock.py b/buildtrigger/test/gitlabmock.py index c7d4a5865..2739a539f 100644 --- a/buildtrigger/test/gitlabmock.py +++ b/buildtrigger/test/gitlabmock.py @@ -4,11 +4,11 @@ from mock import Mock from buildtrigger.gitlabhandler import GitLabBuildTrigger from util.morecollections import AttrDict -def get_gitlab_trigger(subdir=''): +def get_gitlab_trigger(dockerfile_path=''): trigger_obj = AttrDict(dict(auth_token='foobar', id='sometrigger')) trigger = GitLabBuildTrigger(trigger_obj, { 'build_source': 'foo/bar', - 'subdir': subdir, + 'dockerfile_path': dockerfile_path, 'username': 'knownuser' }) diff --git a/buildtrigger/test/test_basehandler.py b/buildtrigger/test/test_basehandler.py index 5bf95c41b..7162c2535 100644 --- a/buildtrigger/test/test_basehandler.py +++ b/buildtrigger/test/test_basehandler.py @@ -13,3 +13,43 @@ from buildtrigger.basehandler import BuildTriggerHandler ]) def test_path_is_dockerfile(input, output): assert BuildTriggerHandler.filename_is_dockerfile(input) == output + + +@pytest.mark.parametrize('input,output', [ + ("", {}), + ("/a", {"/a": ["/"]}), + ("a", {"/a": ["/"]}), + ("/b/a", {"/b/a": ["/b", "/"]}), + ("b/a", {"/b/a": ["/b", "/"]}), + ("/c/b/a", {"/c/b/a": ["/c/b", "/c", "/"]}), + ("/a//b//c", {"/a/b/c": ["/", "/a", "/a/b"]}), + ("/a", {"/a": ["/"]}), +]) +def test_subdir_path_map_no_previous(input, output): + actual_mapping = BuildTriggerHandler.get_parent_directory_mappings(input) + for key in actual_mapping: + value = actual_mapping[key] + actual_mapping[key] = value.sort() + for key in output: + value = output[key] + output[key] = value.sort() + + assert actual_mapping == output + + +@pytest.mark.parametrize('new_path,original_dictionary,output', [ + ("/a", {}, {"/a": ["/"]}), + ("b", {"/a": ["some_path", "another_path"]}, {"/a": ["some_path", "another_path"], "/b": ["/"]}), + ("/a/b/c/d", {"/e": ["some_path", "another_path"]}, + {"/e": ["some_path", "another_path"], "/a/b/c/d": ["/", "/a", "/a/b", "/a/b/c"]}), +]) +def test_subdir_path_map(new_path, original_dictionary, output): + actual_mapping = BuildTriggerHandler.get_parent_directory_mappings(new_path, original_dictionary) + for key in actual_mapping: + value = actual_mapping[key] + actual_mapping[key] = value.sort() + for key in output: + value = output[key] + output[key] = value.sort() + + assert actual_mapping == output diff --git a/buildtrigger/test/test_bitbuckethandler.py b/buildtrigger/test/test_bitbuckethandler.py index 320a18906..c4ebb4ac7 100644 --- a/buildtrigger/test/test_bitbuckethandler.py +++ b/buildtrigger/test/test_bitbuckethandler.py @@ -16,13 +16,13 @@ def test_list_build_subdirs(bitbucket_trigger): assert bitbucket_trigger.list_build_subdirs() == ["/Dockerfile"] -@pytest.mark.parametrize('subdir, contents', [ +@pytest.mark.parametrize('dockerfile_path, contents', [ ('/Dockerfile', 'hello world'), ('somesubdir/Dockerfile', 'hi universe'), ('unknownpath', None), ]) -def test_load_dockerfile_contents(subdir, contents): - trigger = get_bitbucket_trigger(subdir) +def test_load_dockerfile_contents(dockerfile_path, contents): + trigger = get_bitbucket_trigger(dockerfile_path) assert trigger.load_dockerfile_contents() == contents diff --git a/buildtrigger/test/test_githubhandler.py b/buildtrigger/test/test_githubhandler.py index db236048c..3d8ac2763 100644 --- a/buildtrigger/test/test_githubhandler.py +++ b/buildtrigger/test/test_githubhandler.py @@ -68,13 +68,13 @@ def test_handle_trigger_request(github_trigger, payload, expected_error, expecte assert isinstance(github_trigger.handle_trigger_request(request), PreparedBuild) -@pytest.mark.parametrize('subdir, contents', [ +@pytest.mark.parametrize('dockerfile_path, contents', [ ('/Dockerfile', 'hello world'), ('somesubdir/Dockerfile', 'hi universe'), ('unknownpath', None), ]) -def test_load_dockerfile_contents(subdir, contents): - trigger = get_github_trigger(subdir) +def test_load_dockerfile_contents(dockerfile_path, contents): + trigger = get_github_trigger(dockerfile_path) assert trigger.load_dockerfile_contents() == contents diff --git a/buildtrigger/test/test_gitlabhandler.py b/buildtrigger/test/test_gitlabhandler.py index a74f5fea2..6f6555769 100644 --- a/buildtrigger/test/test_gitlabhandler.py +++ b/buildtrigger/test/test_gitlabhandler.py @@ -16,13 +16,13 @@ def test_list_build_subdirs(gitlab_trigger): assert gitlab_trigger.list_build_subdirs() == ['/Dockerfile'] -@pytest.mark.parametrize('subdir, contents', [ +@pytest.mark.parametrize('dockerfile_path, contents', [ ('/Dockerfile', 'hello world'), ('somesubdir/Dockerfile', 'hi universe'), ('unknownpath', None), ]) -def test_load_dockerfile_contents(subdir, contents): - trigger = get_gitlab_trigger(subdir) +def test_load_dockerfile_contents(dockerfile_path, contents): + trigger = get_gitlab_trigger(dockerfile_path) assert trigger.load_dockerfile_contents() == contents diff --git a/data/migrations/versions/a6c463dfb9fe_back_fill_build_expand_config.py b/data/migrations/versions/a6c463dfb9fe_back_fill_build_expand_config.py new file mode 100644 index 000000000..44701f959 --- /dev/null +++ b/data/migrations/versions/a6c463dfb9fe_back_fill_build_expand_config.py @@ -0,0 +1,85 @@ +"""back fill build expand_config + +Revision ID: a6c463dfb9fe +Revises: e2894a3a3c19 +Create Date: 2017-03-17 10:00:19.739858 + +""" + +# revision identifiers, used by Alembic. +import json +import os + +from data.database import RepositoryBuildTrigger + +revision = 'a6c463dfb9fe' +down_revision = 'e2894a3a3c19' + +from alembic import op + + +def upgrade(tables): + repostioryBuildTriggers = RepositoryBuildTrigger.select() + for repositoryBuildTrigger in repostioryBuildTriggers: + config = json.loads(repositoryBuildTrigger.config) + repositoryBuildTrigger.config = json.dumps(get_config_expand(config)) + + +def downgrade(tables): + repostioryBuildTriggers = RepositoryBuildTrigger.select() + for repositoryBuildTrigger in repostioryBuildTriggers: + config = json.loads(repositoryBuildTrigger.config) + repositoryBuildTrigger.config = json.dumps(get_config_expand(config)) + + +def create_context(current_subdir): + if current_subdir == "": + current_subdir = os.path.sep + current_subdir + + if current_subdir[len(current_subdir) - 1] != os.path.sep: + current_subdir += os.path.sep + + context, _ = os.path.split(current_subdir) + return context + + +def create_dockerfile_path(current_subdir): + if current_subdir == "": + current_subdir = os.path.sep + current_subdir + + if current_subdir[len(current_subdir) - 1] != os.path.sep: + current_subdir += os.path.sep + + return current_subdir + "Dockerfile" + + +def get_config_expand(config): + """ A function to transform old records into new records """ + if not config: + return config + + # skip records that have been updated + if "context" in config or "dockerfile_path" in config: + return config + + config_expand = {} + if "subdir" in config: + config_expand = dict(config) + config_expand["context"] = create_context(config["subdir"]) + config_expand["dockerfile_path"] = create_dockerfile_path(config["subdir"]) + + return config_expand + + +def get_config_contract(config): + """ A function to delete context and dockerfile_path from config """ + if not config: + return config + + if "context" in config: + del config["context"] + + if "dockerfile_path" in config: + del config["dockerfile_path"] + + return config diff --git a/data/model/build.py b/data/model/build.py index 605259e14..e67eaffe5 100644 --- a/data/model/build.py +++ b/data/model/build.py @@ -18,10 +18,14 @@ PHASES_NOT_ALLOWED_TO_CANCEL_FROM = (BUILD_PHASE.PUSHING, BUILD_PHASE.COMPLETE, ARCHIVABLE_BUILD_PHASES = [BUILD_PHASE.COMPLETE, BUILD_PHASE.ERROR, BUILD_PHASE.CANCELLED] -def update_build_trigger(trigger, config, auth_token=None): +def update_build_trigger(trigger, config, auth_token=None, write_token=None): trigger.config = json.dumps(_get_config_expand(config or {})) if auth_token is not None: trigger.auth_token = auth_token + + if write_token is not None: + trigger.write_token = write_token + trigger.save() diff --git a/data/model/helpers/build_helper.py b/data/model/helpers/build_helper.py index 7de9da254..1f5da8296 100644 --- a/data/model/helpers/build_helper.py +++ b/data/model/helpers/build_helper.py @@ -1,44 +1,9 @@ -import os - def _get_config_expand(config): """ Get config with both context and dockerfile_path written to it """ - if config and "subdir" in config and "context" not in config: - config_expand = dict(config) - config_expand["context"] = _create_context(config["subdir"]) - config_expand["dockerfile_path"] = _create_dockerfile_path(config["subdir"]) - return config_expand - return config or {} + if not config: + return {} + if 'context' in config: + config['subdir'] = config['context'] - -def _create_context(current_subdir): - """ Create context from current subdir """ - if current_subdir.endswith("Dockerfile"): - context, _ = os.path.split(current_subdir) - if context == "": - return os.path.sep - - return context - - if current_subdir == "": - current_subdir = os.path.sep + current_subdir - - if current_subdir[len(current_subdir) - 1] != os.path.sep: - current_subdir += os.path.sep - - context, _ = os.path.split(current_subdir) - return context - - -def _create_dockerfile_path(current_subdir): - """ Create dockefile path from current subdir """ - if current_subdir.endswith("Dockerfile"): - return current_subdir - - if current_subdir == "": - current_subdir = os.path.sep + current_subdir - - if current_subdir[len(current_subdir) - 1] != os.path.sep: - current_subdir += os.path.sep - - return current_subdir + "Dockerfile" + return config diff --git a/data/model/helpers/test/test_build_helper.py b/data/model/helpers/test/test_build_helper.py index cfb792d6f..25686faff 100644 --- a/data/model/helpers/test/test_build_helper.py +++ b/data/model/helpers/test/test_build_helper.py @@ -4,27 +4,11 @@ from data.model.helpers.build_helper import _get_config_expand @pytest.mark.parametrize('org_config,expected', [ - # Empty config (None, {}), - - # No subdir in config ({}, {}), - - ({"subdir": ""}, {"context": "/", "dockerfile_path": "/Dockerfile", "subdir": ""}), - ({"subdir": "/"}, {"context": "/", "dockerfile_path": "/Dockerfile", "subdir": "/"}), - ({"subdir": "/Dockerfile"}, {"context": "/", "dockerfile_path": "/Dockerfile", "subdir": "/Dockerfile"}), - ({"subdir": "a"}, {"context": "a", "dockerfile_path": "a/Dockerfile", "subdir": "a"}), - ({"subdir": "Dockerfile"}, {"context": "/", "dockerfile_path": "Dockerfile", "subdir": "Dockerfile"}), - ({"subdir": "server.Dockerfile"}, - {"context": "/", "dockerfile_path": "server.Dockerfile", "subdir": "server.Dockerfile"}), - ({"subdir": "/a/b/Dockerfile"}, - {"context": "/a/b", "dockerfile_path": "/a/b/Dockerfile", "subdir": "/a/b/Dockerfile"}), - ({"subdir": "/a/b/server.Dockerfile"}, - {"context": "/a/b", "dockerfile_path": "/a/b/server.Dockerfile", "subdir": "/a/b/server.Dockerfile"}), - ({"subdir": "a/b/Dockerfile"}, {"context": "a/b", "dockerfile_path": "a/b/Dockerfile", "subdir": "a/b/Dockerfile"}), - ({"subdir": "a/b/server.Dockerfile"}, - {"context": "a/b", "dockerfile_path": "a/b/server.Dockerfile", "subdir": "a/b/server.Dockerfile"}), - ({"subdir": "/a/b/c", "context": "slime"}, {"context": "slime", "subdir": "/a/b/c"}), + ({'some other key': 'some other value'}, {'some other key': 'some other value'}), + ({'context': 'some/context', 'dockerfile_path': 'some/context/with/Dockerfile'}, + {'context': 'some/context', 'dockerfile_path': 'some/context/with/Dockerfile', 'subdir': 'some/context'}), ]) def test_super_user_build_endpoints(org_config, expected): assert _get_config_expand(org_config) == expected diff --git a/endpoints/api/build.py b/endpoints/api/build.py index 1bb575a4f..1a0d1c89c 100644 --- a/endpoints/api/build.py +++ b/endpoints/api/build.py @@ -1,5 +1,5 @@ """ Create, list, cancel and get status/logs of repository builds. """ - +import os from urlparse import urlparse import logging @@ -51,6 +51,7 @@ def user_view(user): 'is_robot': user.robot, } + def trigger_view(trigger, can_read=False, can_admin=False, for_build=False): if trigger and trigger.uuid: build_trigger = BuildTriggerHandler.get_handler(trigger) @@ -133,6 +134,8 @@ def build_status_view(build_obj): 'display_name': build_obj.display_name, 'status': status or {}, 'subdirectory': job_config.get('build_subdir', ''), + 'dockerfile_path': job_config.get('build_subdir', ''), + 'context': job_config.get('context', ''), 'tags': job_config.get('docker_tags', []), 'manual_user': job_config.get('manual_user', None), 'is_writer': can_write, @@ -244,6 +247,7 @@ class RepositoryBuildList(RepositoryParamResource): raise InvalidRequest('Invalid Archive URL: Must be http or https') subdir = request_json['subdirectory'] if 'subdirectory' in request_json else '' + context = request_json['context'] if 'context' in request_json else os.path.dirname(subdir) tags = request_json.get('docker_tags', ['latest']) pull_robot_name = request_json.get('pull_robot', None) @@ -291,9 +295,9 @@ class RepositoryBuildList(RepositoryParamResource): prepared.archive_url = archive_url prepared.tags = tags prepared.subdirectory = subdir + prepared.context = context prepared.is_manual = True prepared.metadata = {} - try: build_request = start_build(repo, prepared, pull_robot_name=pull_robot_name) except MaximumBuildsQueuedException: diff --git a/endpoints/api/test/test_trigger.py b/endpoints/api/test/test_trigger.py new file mode 100644 index 000000000..48339c9d4 --- /dev/null +++ b/endpoints/api/test/test_trigger.py @@ -0,0 +1,22 @@ +import pytest + +from endpoints.api.trigger import is_parent + + +@pytest.mark.parametrize('context,dockerfile_path,expected', [ + ("/", "/a/b", True), + ("/a", "/a/b", True), + ("/a/b", "/a/b", False), + ("/a//", "/a/b", True), + ("/a", "/a//b/c", True), + ("/a//", "a/b", True), + ("/a/b", "a/bc/d", False), + ("/d", "/a/b", False), + ("/a/b", "/a/b.c", False), + ("/a/b", "/a/b/b.c", True), + ("", "/a/b.c", False), + ("/a/b", "", False), + ("", "", False), +]) +def test_super_user_build_endpoints(context, dockerfile_path, expected): + assert is_parent(context, dockerfile_path) == expected diff --git a/endpoints/api/trigger.py b/endpoints/api/trigger.py index 2698bbb3b..1bb885878 100644 --- a/endpoints/api/trigger.py +++ b/endpoints/api/trigger.py @@ -2,17 +2,21 @@ import json import logging - +from os import path from urllib import quote from urlparse import urlunparse from flask import request, url_for from app import app +from auth.permissions import (UserAdminPermission, AdministerOrganizationPermission, + ReadRepositoryPermission, AdministerRepositoryPermission) from buildtrigger.basehandler import BuildTriggerHandler from buildtrigger.triggerutil import (TriggerDeactivationException, TriggerActivationException, EmptyRepositoryException, RepositoryReadException, TriggerStartException) +from data import model +from data.model.build import update_build_trigger from endpoints.api import (RepositoryParamResource, nickname, resource, require_repo_admin, log_action, request_error, query_param, parse_args, internal_only, validate_json_request, api, path_param, abort, @@ -20,12 +24,9 @@ from endpoints.api import (RepositoryParamResource, nickname, resource, require_ from endpoints.exception import NotFound, Unauthorized, InvalidRequest from endpoints.api.build import build_status_view, trigger_view, RepositoryBuildStatus from endpoints.building import start_build, MaximumBuildsQueuedException -from data import model -from auth.permissions import (UserAdminPermission, AdministerOrganizationPermission, - ReadRepositoryPermission, AdministerRepositoryPermission) -from util.names import parse_robot_username +from endpoints.exception import NotFound, Unauthorized, InvalidRequest from util.dockerfileparse import parse_dockerfile - +from util.names import parse_robot_username logger = logging.getLogger(__name__) @@ -131,19 +132,25 @@ class BuildTriggerSubdirs(RepositoryParamResource): try: subdirs = handler.list_build_subdirs() + context_map = {} + for file in subdirs: + context_map = handler.get_parent_directory_mappings(file, context_map) + return { - 'subdir': ['/' + subdir for subdir in subdirs], - 'status': 'success' + 'dockerfile_paths': ['/' + subdir for subdir in subdirs], + 'contextMap': context_map, + 'status': 'success', } except EmptyRepositoryException as exc: return { 'status': 'success', - 'subdir': [] + 'contextMap': {}, + 'dockerfile_paths': [], } except RepositoryReadException as exc: return { 'status': 'error', - 'message': exc.message + 'message': exc.message, } else: raise Unauthorized() @@ -235,9 +242,7 @@ class BuildTriggerActivate(RepositoryParamResource): raise request_error(message=exc.message) # Save the updated config. - trigger.config = json.dumps(final_config) - trigger.write_token = write_token - trigger.save() + update_build_trigger(trigger, final_config, write_token=write_token) # Log the trigger setup. repo = model.repository.get_repository(namespace_name, repo_name) @@ -343,6 +348,15 @@ class BuildTriggerAnalyze(RepositoryParamResource): 'message': 'Could not parse the Dockerfile specified' } + # Check whether the dockerfile_path is correct + if new_config_dict.get('context'): + if not is_parent(new_config_dict.get('context'), new_config_dict.get('dockerfile_path')): + return { + 'status': 'error', + 'message': 'Dockerfile, %s, is not child of the context, %s.' % + (new_config_dict.get('context'), new_config_dict.get('dockerfile_path')) + } + # Default to the current namespace. base_namespace = namespace_name base_repository = None @@ -399,6 +413,28 @@ class BuildTriggerAnalyze(RepositoryParamResource): raise NotFound() +def is_parent(context, dockerfile_path): + """ This checks whether the context is a parent of the dockerfile_path""" + if context == "" or dockerfile_path == "": + return False + + normalized_context = path.normpath(context) + if normalized_context[len(normalized_context) - 1] != path.sep: + normalized_context += path.sep + + if normalized_context[0] != path.sep: + normalized_context = path.sep + normalized_context + + normalized_subdir = path.normpath(path.dirname(dockerfile_path)) + if normalized_subdir[0] != path.sep: + normalized_subdir = path.sep + normalized_subdir + + if normalized_subdir[len(normalized_subdir) - 1] != path.sep: + normalized_subdir += path.sep + + return normalized_subdir.startswith(normalized_context) + + @resource('/v1/repository//trigger//start') @path_param('repository', 'The full path of the repository. e.g. namespace/name') @path_param('trigger_uuid', 'The UUID of the build trigger') @@ -418,7 +454,7 @@ class ActivateBuildTrigger(RepositoryParamResource): 'description': '(Custom Only) If specified, the ref/SHA1 used to checkout a git repository.' }, 'refs': { - 'type': ['object', 'null'], + 'type': ['object', 'null'], 'description': '(SCM Only) If specified, the ref to build.' } }, @@ -467,6 +503,7 @@ class ActivateBuildTrigger(RepositoryParamResource): @path_param('trigger_uuid', 'The UUID of the build trigger') class TriggerBuildList(RepositoryParamResource): """ Resource to represent builds that were activated from the specified trigger. """ + @require_repo_admin @disallow_for_app_repositories @parse_args() @@ -483,10 +520,12 @@ class TriggerBuildList(RepositoryParamResource): FIELD_VALUE_LIMIT = 30 + @resource('/v1/repository//trigger//fields/') @internal_only class BuildTriggerFieldValues(RepositoryParamResource): """ Custom verb to fetch a values list for a particular field name. """ + @require_repo_admin @disallow_for_app_repositories @nickname('listTriggerFieldValues') @@ -558,13 +597,13 @@ class BuildTriggerSources(RepositoryParamResource): raise Unauthorized() - @resource('/v1/repository//trigger//namespaces') @path_param('repository', 'The full path of the repository. e.g. namespace/name') @path_param('trigger_uuid', 'The UUID of the build trigger') @internal_only class BuildTriggerSourceNamespaces(RepositoryParamResource): """ Custom verb to fetch the list of namespaces (orgs, projects, etc) for the trigger config. """ + @require_repo_admin @disallow_for_app_repositories @nickname('listTriggerBuildSourceNamespaces') diff --git a/endpoints/building.py b/endpoints/building.py index a1e17897d..8515287a1 100644 --- a/endpoints/building.py +++ b/endpoints/building.py @@ -54,6 +54,7 @@ def start_build(repository, prepared_build, pull_robot_name=None): 'docker_tags': prepared_build.tags, 'registry': host, 'build_subdir': prepared_build.subdirectory, + 'context': prepared_build.context, 'trigger_metadata': prepared_build.metadata or {}, 'is_manual': prepared_build.is_manual, 'manual_user': get_authenticated_user().username if get_authenticated_user() else None, @@ -122,6 +123,7 @@ class PreparedBuild(object): self._tags = None self._build_name = None self._subdirectory = None + self._context = None self._metadata = None self._trigger = trigger self._is_manual = None @@ -224,6 +226,20 @@ class PreparedBuild(object): self._subdirectory = value + @property + def context(self): + if self._context is None: + raise Exception('Missing property context') + + return self._context + + @context.setter + def context(self, value): + if self._context: + raise Exception('Property context already set') + + self._context = value + @property def metadata(self): if self._metadata is None: diff --git a/initdb.py b/initdb.py index 0a284ae4a..03bb9131c 100644 --- a/initdb.py +++ b/initdb.py @@ -605,6 +605,8 @@ def populate_database(minimal=False, with_storage=False): trigger.config = json.dumps({ 'build_source': 'jakedt/testconnect', 'subdir': '', + 'dockerfile_path': 'Dockerfile', + 'context': '/', }) trigger.save() diff --git a/static/directives/repo-view/repo-panel-builds.html b/static/directives/repo-view/repo-panel-builds.html index 327846002..f59fb8361 100644 --- a/static/directives/repo-view/repo-panel-builds.html +++ b/static/directives/repo-view/repo-panel-builds.html @@ -119,6 +119,7 @@ Trigger Name Dockerfile Location + Context Location Branches/Tags Pull Robot @@ -133,7 +134,8 @@
- {{ trigger.config.subdir || '/' }} + {{ trigger.config.dockerfile_path || '/Dockerfile' }} + {{ trigger.config.context || '/' }} {{ trigger.config.branchtag_regex || 'All' }} diff --git a/static/js/directives/ui/context-path-select/context-path-select.component.html b/static/js/directives/ui/context-path-select/context-path-select.component.html new file mode 100644 index 000000000..dac40463a --- /dev/null +++ b/static/js/directives/ui/context-path-select/context-path-select.component.html @@ -0,0 +1,33 @@ +
+ + +
+
+ Path is an invalid context. +
+
+
diff --git a/static/js/directives/ui/context-path-select/context-path-select.component.spec.ts b/static/js/directives/ui/context-path-select/context-path-select.component.spec.ts new file mode 100644 index 000000000..033ce4945 --- /dev/null +++ b/static/js/directives/ui/context-path-select/context-path-select.component.spec.ts @@ -0,0 +1,87 @@ +import { ContextPathSelectComponent } from './context-path-select.component'; + + +describe("ContextPathSelectComponent", () => { + var component: ContextPathSelectComponent; + var currentContext: string; + var isValidContext: boolean; + var contexts: string[]; + + beforeEach(() => { + component = new ContextPathSelectComponent(); + currentContext = '/'; + isValidContext = false; + contexts = ['/']; + component.currentContext = currentContext; + component.isValidContext = isValidContext; + component.contexts = contexts; + }); + + describe("$onChanges", () => { + + it("sets valid context flag to true if current context is valid", () => { + component.$onChanges({}); + + expect(component.isValidContext).toBe(true); + }); + + it("sets valid context flag to false if current context is invalid", () => { + component.currentContext = "asdfdsf"; + component.$onChanges({}); + + expect(component.isValidContext).toBe(false); + }); + }); + + describe("setcontext", () => { + var newContext: string; + + beforeEach(() => { + newContext = '/conf'; + }); + + it("sets current context to given context", () => { + component.setContext(newContext); + + expect(component.currentContext).toEqual(newContext); + }); + + it("sets valid context flag to true if given context is valid", () => { + component.setContext(newContext); + + expect(component.isValidContext).toBe(true); + }); + + it("sets valid context flag to false if given context is invalid", () => { + component.setContext("asdfsadfs"); + + expect(component.isValidContext).toBe(false); + }); + }); + + describe("setCurrentcontext", () => { + var context: string; + + beforeEach(() => { + context = '/conf'; + }); + + it("sets current context to given context", () => { + component.setSelectedContext(context); + + expect(component.currentContext).toEqual(context); + }); + + it("sets valid context flag to true if given context is valid", () => { + component.setSelectedContext(context); + + expect(component.isValidContext).toBe(true); + }); + + it("sets valid context flag to false if given context is invalid", () => { + component.setSelectedContext("a;lskjdf;ldsa"); + + expect(component.isValidContext).toBe(false); + }); + }); +}); \ No newline at end of file diff --git a/static/js/directives/ui/context-path-select/context-path-select.component.ts b/static/js/directives/ui/context-path-select/context-path-select.component.ts new file mode 100644 index 000000000..4d70992f6 --- /dev/null +++ b/static/js/directives/ui/context-path-select/context-path-select.component.ts @@ -0,0 +1,46 @@ +import { Input, Component } from 'angular-ts-decorators'; + + +/** + * A component that allows the user to select the location of the Context in their source code repository. + */ +@Component({ + selector: 'contextPathSelect', + templateUrl: '/static/js/directives/ui/context-path-select/context-path-select.component.html' +}) +export class ContextPathSelectComponent implements ng.IComponentController { + + // FIXME: Use one-way data binding + @Input('=') public currentContext: string; + @Input('=') public isValidContext: boolean; + @Input('=') public contexts: string[]; + private isUnknownContext: boolean = true; + private selectedContext: string | null = null; + + public $onChanges(changes: ng.IOnChangesObject): void { + this.isValidContext = this.checkContext(this.currentContext, this.contexts); + } + + public setContext(context: string): void { + this.currentContext = context; + this.selectedContext = null; + this.isValidContext = this.checkContext(context, this.contexts); + } + + public setSelectedContext(context: string): void { + this.currentContext = context; + this.selectedContext = context; + this.isValidContext = this.checkContext(context, this.contexts); + } + + private checkContext(context: string = '', contexts: string[] = []): boolean { + this.isUnknownContext = false; + var isValidContext: boolean = false; + + if (context.length > 0 && context[0] === '/') { + isValidContext = true; + this.isUnknownContext = true; + } + return isValidContext; + } +} diff --git a/static/js/directives/ui/manage-trigger-githost/manage-trigger-githost.component.html b/static/js/directives/ui/manage-trigger-githost/manage-trigger-githost.component.html index 6298cc9df..1ecc8e28d 100644 --- a/static/js/directives/ui/manage-trigger-githost/manage-trigger-githost.component.html +++ b/static/js/directives/ui/manage-trigger-githost/manage-trigger-githost.component.html @@ -257,9 +257,10 @@ + is-valid-path="$ctrl.local.hasValidDockerfilePath"> +
+ + +
+
+ {{ $ctrl.local.dockerfileLocations.message }} +
+
+ +
+

Select Context

+ + Please select the context for the docker build + + + + +
+ +
+ Retrieving Dockerfile locations +
+ +
+ this.local.dockerfilePath, (path) => { if (path && this.local.selectedRepository) { - this.checkDockerfilePath(this.local.selectedRepository, path); + this.setPossibleContexts(path); + this.checkDockerfilePath(this.local.selectedRepository, path, this.local.dockerContext); + } + }); + + this.$scope.$watch(() => this.local.dockerContext, (context) => { + if (context && this.local.selectedRepository) { + this.checkDockerfilePath(this.local.selectedRepository, this.local.dockerfilePath, context); } }); @@ -117,7 +124,8 @@ export class ManageTriggerGithostComponent implements ng.IComponentController { public createTrigger(): void { var config: any = { build_source: this.local.selectedRepository.full_name, - subdir: this.local.dockerfilePath.substr(1) // Remove starting / + dockerfile_path: this.local.dockerfilePath, + context: this.local.dockerContext }; if (this.local.triggerOptions.hasBranchTagFilter && @@ -271,6 +279,7 @@ export class ManageTriggerGithostComponent implements ng.IComponentController { private loadDockerfileLocations(repository: any): void { this.local.dockerfilePath = null; + this.local.dockerContext = null; var params = { 'repository': this.repository.namespace + '/' + this.repository.name, @@ -301,7 +310,7 @@ export class ManageTriggerGithostComponent implements ng.IComponentController { []); } - private checkDockerfilePath(repository: any, path: string): void { + private checkDockerfilePath(repository: any, path: string, context: string): void { this.local.triggerAnalysis = null; this.local.robotAccount = null; @@ -312,7 +321,8 @@ export class ManageTriggerGithostComponent implements ng.IComponentController { var config = { 'build_source': repository.full_name, - 'subdir': path.substr(1) + 'dockerfile_path': path.substr(1), + 'context': context }; var data = { @@ -325,6 +335,12 @@ export class ManageTriggerGithostComponent implements ng.IComponentController { this.buildOrderedRobotAccounts(); }, this.ApiService.errorDisplay('Could not analyze trigger')); } + + private setPossibleContexts(path){ + if(this.local.dockerfileLocations.contextMap){ + this.local.contexts = this.local.dockerfileLocations.contextMap[path] || []; + } + } } diff --git a/static/js/quay.module.ts b/static/js/quay.module.ts index e1162bb32..980c5e9c2 100644 --- a/static/js/quay.module.ts +++ b/static/js/quay.module.ts @@ -7,6 +7,7 @@ import { RegexMatchViewComponent } from "./directives/ui/regex-match-view/regex- import { NgModule } from "angular-ts-decorators"; import { QuayRoutes } from "./quay-routes.module"; import { DockerfilePathSelectComponent } from './directives/ui/dockerfile-path-select/dockerfile-path-select.component'; +import { ContextPathSelectComponent } from './directives/ui/context-path-select/context-path-select.component'; import { ManageTriggerCustomGitComponent } from './directives/ui/manage-trigger-custom-git/manage-trigger-custom-git.component'; import { ManageTriggerGithostComponent } from './directives/ui/manage-trigger-githost/manage-trigger-githost.component'; import { LinearWorkflowComponent } from './directives/ui/linear-workflow/linear-workflow.component'; @@ -31,6 +32,7 @@ import { DataFileServiceImpl } from './services/datafile/datafile.service.impl'; declarations: [ RegexMatchViewComponent, DockerfilePathSelectComponent, + ContextPathSelectComponent, ManageTriggerCustomGitComponent, ManageTriggerGithostComponent, LinearWorkflowComponent, diff --git a/test/data/test.db b/test/data/test.db index 874875be81147017b1fbda29f157b47a32b62754..fc8c7f1273e44fc9340d8f548b497e568bc1a139 100644 GIT binary patch delta 69303 zcmeFa33yy*bug^aVoA27v1Q42;v`<8?78yYmn2R+`@Y@T?hMQ1uCwp^q-0_Wp{30N zYPqG*mX?0B{l7v;0jrcHfkGF`5@?|mLJ0KhUYhW=wEfyb{xc&>8d;8Qo_u-w{Lk+^ zL}bo6_uO|m=e+NGmiIpNy!_Df^5aJh&bM7>nR1RHf4%of<pKT!!-$45KJXhgj7)m`ozhINMWSPv9!qH)Y|wtbcX${2`vzw{Ivf0 zsbzoW&Lf))6qc1)lA&NwB>1Djpl~F|At;AyoTP9H1+P22NsWmZMKX+x;DjV0q(s0F zO7J>PB{iARScVd*vlEAA$D)&yGc#vz(@*^6p_Q5IzWz&Zxm9Z2xOUz8gEMz6KY4E` z^tMl|dA{jidvSelpTp(;MvbLJgdj*9c#{&sYB-J%qC_$>#xR6xVAp!T z3$tOo`;Sh2;Cr664}8=A_K%`7M}4Qie>3}HXpQ~ogEL1em6emsgu3=g_;H>76Qx_H z9beEfeaXM}_TNKiPJ3Uw8vU~G8*4v$7G`p8^W_w)C@RH(yipj;q$FX8gsB*!;S?vy z3dJ%qd*{AQY8uldhGuXC#}%N~7!8qBjzKh%VsTXyS&HS>8XrSvR@Bod{x2t6Yrp-m z2WL)Bh&P}5<;WM;KKL#F+Cv`)0e)ub*z8Y#Yvg_P6k&8{hw=fc^6w53C*kUsr{v1E{&&ID${Pye(3m=`ib^MV% z2glFtU7C6C)Zg#hJNxPJZ%>tH-#Pgwdryxm3(Iq3d+wR|(cBjn?%n&Hna|Jv;=<#` z*G>M*#81vv_B=n+TS!k`Zz#?G{@eqTw-_Iu4DQtoPfyP-e0Tiry^qcR;{;~V_x<_a z-<^KV^w%bTZQq-x-#`7*+^_E=J}~{JnI9PbZhX=B#H?fPU+1fP*5{oQNyBwxpPRdF z&!dyw`C}7boyPa56Em~_F!iaKka6GIXPzcjFcFss3`Y?{lVGM57$}%q5n)-FPYUHE zN~PA0ek#5KgNCCLOM{}J975tMiEuQBAsi(u7)#3{sYz?^_|#W!qDVr*WrjdF1_za< zK*u;hA{c{nG=UO?h@nyai{CtS+lS75nw{_wm@IR&tRMuZf<~|!jet2*5Cy|nl;Ys6 z40qqH6Hx7Y8td1dd?^mr{DhNL$|Hr zfB5B_B%07SQ@xc%%62gH-AT%S2IEkW)2&$}~c=ix-lg45cfzx2u$bmMn1ZV?} z;)ukdBFW$=Npc#hhrV+NS^JGYy6q;Kp-74$hYf)-Ls_sPB-juJWLnfTm6k!1^xi)L z`N=>2A1j24F(RiTD9eBXNHT{=ltv*ksj4JSvK)(3x9A`IizCR|kn~i#(pw&XdjMZ3<5fev&S*wE{ePhrB?}-qj6UM+KF)< zz4Q1%cEN{j@ZL0j+r^M? zxoMnV#PMAZ0kh?I+&qq440qqnaV|b{GL5Dj#C&Fc71ZsYJ(l0h*NU4P)zc*A#mmjYdTc!Dr&X*Z)A-!Oi- zzl-X~4HKqqs$J;Amqs7DGMyt{Ke7zP1`zmar`EF-=)!ecc6Do+U(1s`$NM%vje|tT&FLM-m?R}%OXu( z3Z25)$sPDzZJ)R_di?qc)9jW|Z#})K3+Emh|J>ZkE%4@WUVF`?fiO&NJ;&(Z`|a^- zE}{&_Ck-aU+!oy~Mu#s)aOiRb%i9F~i!ZxN+Z02B-D+_G!LA8hyd1%SAwhq;qq}Bt z{{<{|5g0E=uyAa0cEqymVlaQKe`$ib%P`n?8T{Uz_+4{3+rJFK%uf8SVV=GWe$P() zt`X=@U4~$CCw^BCCN6^?zX3-0<+a6to_l=!D|5Tm;MyaTW1~yQag4nSMd5$f9Ge^) znbTbjIx-ZBZ9UsXaQGq&`lEZ!4gI+UhYkI?1i{kILD{W6JMqNv{sr6K4ei-}!>)Nh zu#>~CdEI{*ym2SKzl*`;Rp(Dle-#JA1Hentv?K>4TT=!Y%UUROkPu=8zXV z`&>qK&~xt@|C5(pXr{LokgE&LU_rg)LbKB%?9v5LmQjk9S4Q!>bYb?`WdD)`pe&>KUAh3v%q8)lE2H>b9h`!N_L2m!%#7l9^#GQc zOXA0m!6eU)ZwdVtgRNcJ2giS5ZeMb8Ke~HCnmuxI3gqZg zJG1%2mnE4!ba|3xAOUf?)SG6OE@ZQNjX8KB$*x)5zxc8wTZe@G2QH-9HM!IKPfWq& zY@gtEQ5-j3j^gmbPKt{x2GDu)RZy{&sJ6$qYJs0h3)6*vthK zyM`W}zBEN2XR$pOkn9>(bn0>x*yIHiyPAqlT$EyUvFRn;5hMg_Fz}hPs0z+@jzv(6lVzL`*+E3JRBmL{T0xZ50)`Xh1U^1J7Cm@y)_`j) z#2{z}qK72Bhl6;J2wrwX6*)~MX^c@NRV$Yzu{VwxHlCT47>J3|;L0aNMZFW8=`s$^ zbO_DKETbxvEK;&24IirQ9UB{)G`?=k_=NFcmE=!Yk>{_$?FB*_9ogc$q$&JHF?q?z>TD6cM@yLjV*ZSPnrb5NebmSV^)N42ukbWL1<&0)jv|MN8R|Rvrw>xbcZG7?3BxvHM&wrYM$>F;zkloM9o}CqoPdB9|JXp|YlEs>(1Tb?*M@XHTtM5#{64|1@mV z&bNSSOn=2VXw**lZ+!Pl(;w+WLs$g@YY3`|AoHAtfz(3aRFM@1gE%-ut`w=+EfvIC zTD?QAsbZsClgbSzLAq>lkIQ3?pwUP~z!E+$5o9R4iLfzFABV<*9>(vs)3lAI&AdPc zZGnJ~#GE`$@+uwkM;ms5Rz^Iq?u`LhUSR^1Dp_(M5e@49Qi7<7k%CZ+Lj- z+P;KyB1Gy~m_`VHlPtu^B@j@AfM_nw(u1S{bbAa-YP(!Z*UHec0M1~v$84tUE(#^x z{;Q5Qz`g#kC*eYEkqFBtO)(R0_k{zDL$I-4Pn5PtLp})OYaGn2OhHI>dv?p^!mwR-&Lu>WbkfT5jKw1) zqm&(YGGU5#3Q@sLu&5WaxDp<}7xTnPD&%4?KStXfW|;A?IY#lD0~Q^fT!?#e37p zec_Rr*=?bQ31JBcnd<-emYHi-Ao!_5XqQH04eT*Var9t9a72UPFM+ZkC%C5H_ER&| z4h9TOqJ&=lDHu0}Bt)EM33v<^0hm!0?2iUvdko^0pqR8KljUZko>f$s%N1N)!SI!M zB_B__1i{<#)|GOSWdm-T8t}M-)mEXJ2t`hX<(e&&?pQn-Yh2|e%3gB9a)O^}WW-Xw zzABfC=LhcKh*n^$us&fNp&$KeXc#3D8ibZ1wL(Q9uFMYN$}9wzWRzea#!Jv7gvB?M z^n9zZOx#1TIg_avPXy~$63--=Zh%yKi~~(-iEcx{E7nBIiY46LxP!HMJ)%OF%JS}3 zL8JPcMg&`h*cibMrt|Dgv!JWjoSnLDb`0ioQ&({U0@Ebq$UrUygvw=f&}R5Ah_AC4 zgHkv{;?Ph+F?X}v7Gf1AAyAG2E0(x?DS&GoENC~k z%VoxgTS{f@qAlHSC_ykR%ISr-&Rm;gNr=+J3IfRkGz=dOi;D;*0U==J#TXfKT1cr` zYN@rXmQ|JW4YPJLepA`m%G(M))GwP`WZPc|cuW;n+F@(f*-AB*OQST?acTueJ{NED z0e@G2@vSq%NW+F&Vyh@cauhijuiK_!yiQy=UJ~R;aWo2>6B6R*qy#xB1FHfehruXe z8mx(GO)M$pVza(AX03?aig&V9Ip-?*R2va1QC;(@U2dt3RifnSTd7dTjk*G*AmQnH z)YVScZwaZTGI_}{LsuCFg@mR-WAE4ljpZ&h!~|r?P$CMg!^y#_!En%Qnu6sTHdHc6 zi8MxPHML*Q3>tgK`AyT%wzx?@&buvs#+3BX0Rr<`ZJ{XXBsqb^Lw>It^$0cx7-G(w zbmCSk6Ha2hlgFI`9`Os5J-KOHwoMB(7jhC%YS3I_3YvTD?9_cjS?q5LqsCDbEU%y^ z&^WLzAcrCYIaoNOFcS0>l7|>*??!Vsr=1KoOSPD0!FiKiV`Qd6XS;z|qun&y)TSe9 zDF;mlnPn-d?VBOTv@QSyCO=K>(Q2_ zdWx>)RNCZ>YN1lP8b{5!#oDSB>n6lZ0I%lTt4g|5)PM7lnG@T#4_zIYHHy@~@d%h0 zloUzW!NO`LqM*1KWV--&STi_Wl@*pFQIS&ARzs8u>K#gBIH^?97Wer{Psr!wFe~Bo zQ>?{hj?o@2%n=T^7qxn5hYcfLnA>3s;2zKf8|(=jVVBKKx_NwLQu|wjg>hhbxxuW` z<1kp)pPh1S4%W3D?hYN&^n8gcy-kIigu@rI3{A+>-e*MIof z%)b3|#)lSeo{ucNH2?hkFRm}WXT~<)f2G^B1GdSt^M*_9pf0(A>Yv|1?H{svrQ03j zkjsy=Ia#{c?nKXjVCJ1?Ua1|@txwFD&b(5=U;peAGo>?zhu>yeT6exU^8?>2p0Nks z;F+2pGZ@a!AJ{+f<4;_F=hhdvO`hjwoOq@42h1kUGh3K=nQ81_@g@_v!O?rz(LEEd zbT)w*oXLTib^gG_#4DcFAKf>*_xPOgqsH47LdNRCcNX3Wr_<95ea95ci(%rGYUQ5u ze16>N?O%AZnL5wtmCo$sc|I>Qshe}!zwF6j;yj~QI-leA!AQ?uFq8O?zcQVx%zpOR zlGPfbyx9+ zn-^lNhjy7DvpZq)F=n0$I0C#7LT%P4WwS@zydxOYpL%NcCf)heY~WTd;tNJ_)*NB% zHUY+ne9e#8G*I}kj%9y`AG;a^!%Rh*?XQAIEc zgGCAFC`dnL2ismug;aHh!)5l|(|f!3^<`Mv8OVDlWys7Vxj|OA3cDi|ax`&{ASgn{ zC&y2X&x{$OkTPeOgQQtj)gTjG9=g=DL1MT9hb$W078uTI93-YrPMk6fA25u8BN_6n zA#)Zoa2XYrYFPz;4h$Wu^9whOEsc?e^_j}P zBMVbw)zVqB{@kC<<<=j2aG&qM%9XtE!_#|?ukZQbz8@W%8DIFon6b0)0nq4QTfojb z#`lgz4YO~YHO-6-+%|)g8061FUrBtBP7KMl2pqOGLPA+i)ENES&+VgOPv{eC#|LfR z+#KS^AypSe2dRaPa!L^!>L8^s(p2v}4qM%AYIf*C+N6HNNVm_2aPd_fgY0tp?37{H zva?grGLjL&H_394N

tEwd_|d>}O)!D*3%%w$HvaZLZMZ|}38muPUm4j7>{hMl{A z&xeeOE9u+NsdmAuF8ZbozugbUj6X2`=lX%C_xyu#8N_OQa>6jXuz~xxG2?$3|84z1 zZ~}0X6SIc#iHX_S4czaK8NX}%eHj0BqwC_CGMi)YvY9d)J=^`N^?y4x_p$l?TU&N; z)lo2m`lnCNN!uId0Tf1<)2Vysh6A>-zh_q&IEAB({<9Z=DcHF)wEma(&bdaJ-~`IB zn11@cQ6|hP1;;54)qm~+FpfbP2L2oWaNpdGqfA)H@B_cce@S!EemTS!LJnt&uPD~E z%NkC!%3LPp;;;@crDe0dLzum+BLjz9x7*Jq#b{ej%jtNd9HEFhAE{T`d2k4rNimV2 z*t!(&rMSAM=(Uk_!jyGI^65sFl*4$2K~jo^=T%=cnD$V4ZzJF@R~2tRQwS$RSk>Xo z7YZa}w-;)4P2xqB<&<_X5Vk~=Zr+x6B>nb6)$hv$;3QrpT#gK*m7-yxC8i{k5bQcR z0pW@Id zf=|F!t$KZ&H(lv8Tv9ko~O0mY#(8)JEutU){ryGiSXt4Rim8%<=tRT1@An zA%V;bNxsufhNQNWFnkcwqI0w&+VD|d9|eyTHP8e zdOC=wo{8GCd?szlQ4HRQXDV1G8Hl~?w7_YLqNy#*cmQlHEYKqXCH*OFPVJ*?vf$w} ziZ_ULO&)>5d$pEX%3x8fZub@JGTwBwlIDV`Ao;Ut-pe)0&1l_^2GDBQm(RBdpBinF zzNngS@|-uW6r-xW(nA}zY9&PV$^lNvhrHRCSm4tBa?Obl-HNBenar(*GpcqvWItW^ zrOc)boihcotR(bIQovk9P3=m}r#cmf8?SV;b|_FG!r^$+;jxCSxoTPwF=r}96l4CR zE3QRD9bVMPR5U^2e9s$?7wg%ct034)4o9NFCG%1>mCRT|VN16QXH&`MX*I!F&g3K~ z(r4sk(p+)Sbjt4c)xEg2R`NK(qb}w`WgkM6s^A7Jg_I<2a$54~K*1qaQ?Xi<4p|!J zrcy`?O_s9T>Tw%l4r?g~)Va7FNj%biIc=f9^$Jdep|+3+b^zNoEx(hQ3-<{EC+%9i zB?%!1nlcFiuIdVgtu}Kv5-v(fxfTs%lNr?<_vbtTwx-o>PiXCZAv@jioKc(n!G{|&oaHZ$5f3qL#8I`F4iR8<90c?W-UTMS)xsjCj0)9 z=n0o&PDj%o%vA(Wt4i2tF^MHvr#Xn`I>CaqYl)`#e24R8nLyW8H+wY?QHn@nMYJ+a z6E8%2vOC|kwW5tOTA;dFhc8~h6**zGXN0)g4dJ$^)$GLVokYnT4(DhE>qN@AoC|5V z#eRZdRJIe8yL^gG2o<80QZ%gqb~Tw41SJ@i6HQCX*=E zi(w%haDf2xK{2TLs=lJ7RWHe*yd|9Mq_ALsb+%kZ#ZtyCxSgna5>m!o6*3JqkW3|G zb&C?q=dA&cDtLleui5K#%yHFfj|HoRLMxA#ebHLa+)SDhiH09dTjYMQ=%6|cocHFN zHHSGX#H5(dY>i=T$|hC3{xY~DX0&Kks)>nc*`F!KYViotDrsIWl!-SJnS8Fs#K?XpmT6k> zoX1OsC8=$f-32^v=1o$L$dMf~;#KP5W;d2ZFtejmO68Td+fASyE*v6)PP&nyiV#dF zxvgr<(&qg*=5pm3U&2kk%(O7zf(550_LHw4pBuYl`L>63r7-8}*Xu%=sjy~I%R1^( zDv)YA5^|yhQNxfcS0Qi@7G`@Lg{`!EWK(n|F^NENB2-n`L?xdMLWs$eRay~L_C)RV zT+yX4HJ_veF|3lc_aK7OVgep9nI?P%Q_7qT=CFp{D!BQ9hBdQ3tj`q#*%ayXc55}J znnQY4AF5=tm0sN&>9x=#*KvAkidW0Y*$7iaX*t38sZKrXBb|O<*wMpH<{Sj$@Jy=> z(T_$6Z}}S#(7{t4wCZAOL0_)PdH9sWox*EjmmE@H*|QPp7~RSgDL2{fo20PElqf`s zQV)w-A!6y|nPMgh2M;t+&2{ap$JMB4U8T@;Dx^J=H?hrdx!lfJZNWxIu-PIe)Y{Iu z+O<4*+|2G~M{e?Q33=H`pg00H@Ru@KcbDcueS)zQES=GCRAmeGNJp`xQ8q5b%cTHi zb@H#`8Sh7CYTe)tsQbZHAOfy=u7VcBpx*2hpCkyKeIp+PNee=n&aLBuBMcd>nB} zrD8Z6!N>#`ZJ<_Ld&Z56N;M#~*nk$yC9-%)?N=LoCf+W)X|KoIsLQpC6YFTn zSR^HqK0aTn#-iKbR4Ve@) zY|i!H$`lP=n2?Q1PiT!;(@JWocG{l4bqY;{4q=LX*M8tUx_FF*r`b2_DYn z3WqQXNfRR$Q~irY=(&n% zP~HqnHH6LKa1CXua3qI1S`LF#J`}JMoJ{JcO7qJrjHJQQTUHTBL4gkAkdy*AXe+8n z5a)*ID2wZv(!6N}LKCD)Lka;0wXB1xW#9?rvxo#9aTH=|aPpV+FO=qQUC{`Xf>S({ zmcl&2Aj**c0dWTelfcmr1>-Cs()u;!`8%Kvl#(e(+(97y1)^b!2xaynWQuSw@EZ64 z2wFt-OnH9gMhHMbhajZ{qNFfG5;Ow^M?93{s|*C32l+hu6XkhiMTL56$Q?lus9lAa z6#?NYPK1ha4uVyjh^iuN8qZbc|NN;N;S8sek_OW#N$_$)1k(+v2OwAA*;m2M&ob-( zacbea$F7L-rNF|AU)nxZgI5CH$IucnL-nl_wG!=pJ|BLK!}9#r6fK_4@A=U+GXow@GZ#J+d#o0xrJ);{yQ z)Bn2X=+qNarzSr&83vMn9Y4R`|0ma9b5n7lJVe)D{9}l$BkQwFC*i;`d$;x^%>JWOV}_X%6C;pohmiRZ$TdUA!U*KZ5VB_ka(D=t8i5=dLbfYFnf=Q{ z$ZUUvXlV%AE)`++9|XwkiIJ?${fk4I?F>+6|A8T7JBgmzzkdjs+2(9CPC*vLiEY4f za8Ma$OwbDmgbhnywoWh9DoW!RNgSUal8khHbH_%mg13$&sN-`(ijnqiQ?TH3pvmL= zh7=>+-lhP_0!PE~{@x+U$SiD=&^WlN@Z+;XitXZxt)`Nz6m0Ve`uNO{Vx+^{JP4M- zSnBxnkYZ#YH!1q$DwIPKG<|%}kYr>UH%W+90?ZqMADU;F!(#Of8b(UfB(0Q-!=6}X`&;0YoqYDQY{$u`=3x6^1Soq!f_b=%4 zUs`C*XBVRLy#*3{!qfBp{gZ|hh7r4U-4L*?+04-)V8no4I|Pg@9oGy2BPQp_5HMn4 z4i5n%2K3Mn0LF03a4!!bBUX562pF+-2Zw+WgR(dTjF_ndL%@gu-EW+n9o5c_8pM&N zZ`E>b$@;J$ZKSuGR%>&)n7^Qmacf=%8JUCCVs1z<(${Sc=LLR9aDK@91Kr)GI4|=< zic2gNvqOTBj&5@}FZM%%k-^(0I4}4^f<5@Q0okP3lKmmY$RutPoR|I~!N~A$5^Rb8 zkYGfVwh7KF!12j@4JO0Llx|a;*MXe{iCZHzh*=Dj;KTcW-S`@_&*7(iJep)90`GI% zIe*CPu%dP{Lb)g_9*R&w*33DRZnqFO#jS2S=;sM@GRX&HXfPTjw&!YV&j=e&xCV(s z*IzR3|CI5}D}4h*4qrO9{!(heu>T#w{d*>pW5y(?U(iULz0oi?7M(TB-ZXYLK4!S0 zTQEME+JA#qj(!bHk^O!Bb;X`p2=w(w?!gM}GC;C)Zc;#n&1pYZ2$u z>yHwPoM9&MmtTMPfP?-#v1nUgq81MsCI=UNt*_FHhYgc&J<5MpcQK3anm;~0m_z^zhTDmJ0C6&**whxn?KdlJUuyhcJ?Sd zo0{C{#rOP+CjBSwUi44OZ-4w_>(AW1C>iGN`(NLdx4P56XYtJXgQo|rxa+-t{oMLH z?_E3sHS}<$*7`^8TYT|d`ER`U-XrMj*!AOvNyvUJK-jk4z?iYe7lZ-k)P)#&?9mO( z>V+6;>}NMH$b}ek?5#tLzW>?9!QV%EQsa?!HI7zF24p?96UZd1`*)FJ1VuZlARxPX8G9c*fpbkH^2vP*g>n#0qs4@ z*Iz)p0oLF4t;MDO0DtrX{A*{2k6n8KI6gB3UvmMNpB{h@9@%Fpj#WjL$e#ieVAvsB9f3O);Od- zG_%F!e8)E62dB5VK*#X;4c2=`f!KNA)F===51iZr4jM5tKJ3WE?7chXY6HGFJ|Ldm zl)+)|mf(u0Yc^mT<1vJudw%g#rzW72>z^?meD|^8f61o~u3q0S=gj3$Q7G7KnG8k- zwW8ADYj~IzkXl* znOmV+y__-EI!&InI6L7Q5iMK&Og7NAQIvow9S?5?FCb|NV{FwUjOCUmzl(Am4KqTDchJWNKu_!__NUfzGrG7;h^nrDPWs1te>Z zOKDRnrclw0DPXoB3Z2%XC7NsmqzFZt5}gL33R03IEf%R&Rcs;JRE)}^;Fi=%)vPi_ zQ!wVU`IHz9>BzLJSR{*7(c!I>Tn?mLu?Acr)URMsi(ks1X*@_|5Pu~t)SurH(6PP}%1Dk}A#F2t z$yCo$>I5Mo>*wXHGwHX{)ke7HlK757T7)j$QIuBI9Ej3fsHt_S9#^af2xpw{74Z&R z(lYi?TWUrsXx-dOX2AJCck}j`)y+6NMJJl_nOwb&+;c}vn$4b1)Et!<+q+Gb;$8%G zCkyUQG1MYc7Bq_66l<^~_V}62RN+BM2n2~Rj=~%i;|bAjXW-oNrEea)=|UFc2Pcr(U8HB0-gezp znf(8wO!lu>CX*coB2sbI!FxiUf`*dKC=SUNd88(iU93f0GMWQ#QFfPBi-w$aF`@<< zj&Ovl#8YWjtfVEohpXfYq*kfftD?o`3Z*D>OJLl2KB2MAQl@J+`AWfv!nK1TOOo>i zW6ny|+HJE9%35U_7LymZRKvV#Y1phGv0W0pp|rVPF*zcIG~?|=>w>qNksZls%F^?+ z*I!I6{p#FRp?-CRy0mmrLtDyvHFu2G@F*^%!*+MR=#Azo9ZKu?tKqmuWV*$;knAP- zM4fYVSTBo&Viv6EO~#=XtYTwbkz`w`NS=zd(B!CighOI%nk@MmrgW*$^mW_G_ZK?-ULYDQcP;6#*Fz+8O1d7;*d0(p*)TPGl{!wLHNI0* zB0|2J$V!!vuj!82IXa$I3x0RSQjH<3$&~NmzDAMGdg|~GV(V67Wdv(GypcBNQ!@bv zV@ee)X2!?c0={V1-jfoMPPm%a@Jd~9CRIGpR62e~+XSi1TESDSa6U*KYPQVgYOvr; zXDP@P2;{YR(#E?@NUdeh$Ydle`2y)`KH7kSDJ2oN(Z!^tWX^OVIf1svJE8R#%U6Z^ zRo^{4cyXatYr%x0QAq{NHX@%CYLN=bTk*QvFXYTvj&vqlRVmeQl*7GfSYlDp;|zy0 z2xKczWoJA}Vct3$Nm6!mEMJA)Ln&uUakaQFm~M4(TTw`8TwY{qg2~zi+gA)*o5^s; z6LX`ba+JyjU9rBsosU*}9)G25QQ9rM>a1~WIOtY=4X*?BBpl&%1oA1EQVuJjk|sFS zWLTggkQnBzN22LUvfPn1oVJ_VrFPIo;&H;ASM9+}j&}#KJX5S&U2bn0)d){LDtT&| zGVFt#S*&e?YU5ar3JEeVq)k~HQpJ6lGR>#D?SP3(l$$PvjrholuUgENGVOfQRg{!w zsansKWLu1{INPa)I}lQ+YKo}kLJ;5QlS$GYEalKrOJ?FA)cvbM{i+M~qZb$IXu;(q z?1{DyGmGJ1BvJRaBW)^I@@Pt@5YVcKj1(G@>{gs!-klCKWz5BuXtl$YH)Dz z8}Zd4pPY}lx-ugX@p#4)rtF?NO$4lboK~&QG*6crblsilAzUTlg^Z4Bp0k#%!Avu2 z*K$p(SN2Hd0Ne57T$@4MnN+Ua=-SF{CC+Bmj+n9)-L){zQGwc4~2XVhvkQ!NCX4)C*siaMwy#EKicK9DxcTd$f)aIY-y{z~ zf7gTrD03Hy^s8vzV-egwG9C~5vPeRri#3h4 zwt|UB&(v+%&3%RQ_0(vaF3^}GRBohfbvf6A9e612bhI=)(dY`%aya5mJ28{VN$`r& zB+Vb!uJ097@uqFoz4}XvvD(JLX_n6)a?- z)q^zN5N-7)s)-^cWb%x?7Q{h9g;>lBmm#=S+8wmz+;Y8Piuz3|d?Bfaq=>GS?Lv7{ zGt~6^iZ!7Y^AjdCCYIw$Js9u6Wmj2es#1Z=W-w16r?4UY#iy2z9k_k(4YPZv|7On{ zrm~ZFj(^_pLH+pCOX}4y-34K~xj~q2E0Xv0eTUBIKm5|t>~)_QgQSt$_kDNo8)koR z<`<^XsZURKC*}>`8~emqdhI~arWc=C`rFB84lmxmp7`?8b%x3R=c8Zx%zF1LOBuu5 zC#F8(++rz~fF<&0OFx`E_iOT-)>pr}lru~|bZ_O)*5CcLCB-l!#J_#VFdq3&Ut6;2 zzOOHRbu#e4(GRWP{1;0QnH+qjN@u^Z+)@M5(ANIXPk&?BxX^P;w)H2zz4YCyUbR;_eDO~g-_|CQb)h2% zJ8n!JoQo>1NVmrX(;U?)Hl0}(H?#44R|%y9c7Zp? zZ6=2$m`GG*OV1RL8>EF&+?JZ$Q|onmHo>Po(Nwb;c>S3P>heIv20V}#IP-{CWv?oHXB~vhcx^|AfBs)&_ol9O9UZU+VNJW z?YH}i(MpM^giSTZnzRMo@DD~gn&2va;pz_HRo?-;j8MC49-?DSK+!@i(p2cG92Dw# zXQ)`n=QVr0Vzb%hy4zH#kgL1uy|}663pH)|mM`o@RZ}pKwTEaPxAxs0GFq?)6w&OeW_Z8N z8MMSt#$;B5c zA&)aos#>BMwpmi0j;+~F6^Xo$PE$fba#%1ZtgU3Ls-x)iSDaj=Aqq`0U-ZQTsMkfe z=t!pE6|qj*%a`~{0Iq*yDiuGj<_l;}LwZPohK>eeYQY;J+nH80*DMh(bGHzW+a#@_ za3S0hEBb0s%A5hAe(9=Ezv@E$G7gAHNoBzK>q&)CG>WuplD}6D*3#x$Ty}UlsGtp^ zJ|dG&*Ng2)U20a-woDMMm`g}B+DzL$g`${3!@U+n?lV@Nl{1zoDxr10E6~}BjS1Dd zQL%u#5L=;=Hg)ppS~K0VAhkx)kq%N)uhUPtAtNAC4Dt@RrV`U4sQ}`@oTiRL3o)ph zN`)c{AqaAm$kwg3O3jVuF;EPyX72=4f{S#+Jv3jjd9!uKZB{!TS6X6hU6BtI{B&BW zkZ^Hf07|?v0@rbSVq(Qtr*f8Bsg!Y)ZDAzm5!$SWisoxhJI%&=R<6KiD>;)*tIE!R zqZRgRs0ojn>rhG?5sE%L6?I!9K~JJubkTlFTo%_^r+eMqDNp$ea>plM$(=`C*?PuGC<0Wc zCq++_hrpwX;l4D8jCGs_gXDxO6+Ii-}FgYLZ$ps7ffSslk)|2nL9Z1@N+1)&y z%ogCH7GE?g*gBLQRjN)?)n#I{wr1Bsc}q55vgfFIeK{emzj$i-&~>+rExdKUHOKD# z^VyHjJT`sH)W;@k6LaG(!!2W9h7fGE5$O= zpS@=}WthC~6UtZD@4I*Tq+x#SKPU5lx5cCX%+i_l58StGHC+8J_SNrVU;Qrj|2Myj z?SDsbd5@u_PY0Lp**7=-@c5XaH})@MB`{{k^>T0-o#E;)yen9~?mqp@zc2gs9}GaJ zX3lY@ufKGie&EfA{Q3hS03A4e|6NB;e)RA48$No(uN(LQ)pNITmWe+N=r0YZ-ZB8; z>L;(WKU#T2e~MoY?3=J`f5QFgZ^0+rkE~yR|H*g$;2-txJq%C0Jq%AAz3)A5zWJu- ze^38hc=^GZ>rYO9`_@0cm(_z2_;u=i>C@(q{Qk%E%*T(wC(+?r^^K3)U&*(s@4R=D z`bs`ntv?inw%mNn`#(kHo?cjc{LB8k?!0G{!IgZ-`s~Er+kHLp?2(n3xwo(X`+;91 zKcyF9&=ssB!aYAHKD&1MD}Mb>Bs z{>cP9fll1HY+-)=di~=tva5IEn_azdMykRToiNJFLLW2 zcr8487ynQ$^u7=M;o6hm_Uq*90J7)FcfMveHTzd<-ve&HH-KQDwBok+{-4D))7M}U zUJsD*&%7@^@%(kS=~x>E>JJ8>!FqV>J&AwQfAsq0#En<*jc>h|g2!(EUgD{Ht~q?K z{=&a5`_J9K{I;t;=U3V1T)JR?+}lW*&&qr;=MOX~tJ&r87pu8stN<5eIqOK14QI+h z$dQTFD)m^>E@-4Lz-LgwY~x#8DwV8vdbI*!vvpE69tTIQ$Te8W(bTGBqlF|P_pMo} zCAp$fYUXJz+NnFJpgj_>2pvk{dj%VxYh^9)*%4I1^2MazV|60QfRJL%Z8H-nHPS9m z&E75BeaW;Xlpx&UfKcWdNvs&6gcM&d`Mfc^w_dHghy+0;TaIY7oV99I1`D)0t$2iS z#}chvheo}WMD^4po8xIBge2=uAL*%@*rMBuFjlKj4VY|9zT5V9;Cd)5j4{nDDVS1` zCQ_EzRIk{rRco!LfYvh23<_awi$pk-guAL`Ly4#*j%DGJm{1_+u$OZ*TygF)Rg;!b zDh!^C^3_A%tA6Nv@Zv)4_4?|ym~4tPTnv#-Cw+)L#Y?3oaAda}udCJvUs6U-?5e79Hk1S*tJE{bKi zQmkml(pFC-V>8ovTA~AP_{4|gwuj-`Br091WVF)W6mu8=F9O{D#mI-5x~L$wfmC&v$-dQHeA{50LLG}m9~FaO$A zL;Wfn>PIgw)3L4z>$dP#t5c)gkpO(*qXMaJN=IVwYA;mHO68)G(%N+-OGTR7ch?CyH_o?J=B!wxJ_3^7PK-H!VqrOq9) zpzU-c$TeGn7Q!mUKGxKdzGx;cm~(`{P>jDb*>H8!pkcHRN;@_tXVfH{Nj6m}WHHcGu(}A8?8ie4WdZ_NombY$JnlDK3PP z7#TLTL|?7zjuF0UIw}a+Iv%XRJq`{wiO7Wv<|NHAnPEHmU_9*U3Sl4KX*WW0w%6~# z<#_gdB+_%0I*m>y7OvL<)_AI^igBDPGDy+xa7SwOM%h!}o&ljui6ng`}(NO&1y+Z>!MG7UF5ROEeU!LD^aGB!?%p_&3=~g8n`61UXQbPi* zxHs%|MW|FgWi^pnAgM#7t^5JXcv4vllzjNPT?tSBIdgiV@Uzk!R zkB@)U@QyJVPO}z0@t)a*N!T$UzF+1{XwaH4sz;g&9O~9ZH=Mg+T>G z1QkU=RB%B-K*v$X1;3NU;x|6dH~r)5nLph>x~p&9dn0bdjeGAo=RNQH(yMpfzx2`G z`~FgT^{#W>mtV6hyF))?!#_83q+NWR*S-0gU7z(%#8182z3>COt~`p{zu{(^dGDS} ztM3K?X43rNuFPWP?e2-Q9^HFB0B*VM*Y3*fFp-VkW@-2rBVM<4?XG|E1~b2T@A?_n z?W%damn7HES%1$z?Aq(`zDfJ1e|GQq$6Z%$U)}foHRil-_xfG!W#h~(zkmK-J$2u? z>-#^nYZqW{u20QxT=9v2fGfWBhFyR3URphwci;NqT~{7md&yrsjmR?`0Pp%e5E-m} zWS8T0+IJtb{`5y?-o&$BEj_Z)N$SR3fQ|X+uEW@vLmiy@>xk>Kc1l<+_s0e!)hA&x z7BA#Aq+JxmT(m!|nz0cGv1xwIC=zwv%1K(cE@#G>UYvuqp^y5TC3K9&S&OsF9H_Bn zFv4$SdSb(A=ggoA# zGj0d+W~z!~X4~KA4{v{meEa)rY|OKh8niY#r7W*Ha1JM0O23kik-b8r91KWe zhht;STp*B-C}hR4$Kh6~(knVP7Urg4sZs;HS}HUCd{izYwwThm7|$y9L{4=Z1Cz9q zExJ76WwWOg)d)uFNyZLVOxU!-*-in(=~N~jSd+OaVnL8TWQS9E+L~}3AyMn7BVfSH zq{;z89_NSz#F=`6iiU7t!w$rv!wS<%w+2C>QcbszX%1+H&{(60N$G%$hLOQE3n>Vo zELRNSSU1x^5N8^pENqk;)YDe1RcmE4ZGbgYONEhKQG+H#uGpjsEc`9W2}YY4vrq9V zNd)@CE;Z~2y7l_hP*Vb7q0`=wi@}oct zBCcOMJgE=)q<-jc8!E*?X{6T$rljNs&9RsZ>g`CLtq;j!u`^{%lJ4;V32SSlV4s+&z1=R%{KV8a98hDJ^;>f zv`mu$Gg0b?_)acdOvy?+L8Wqojuz+o@u&d=;lX6PoCyTsR}LX?#qzLEP!pqo8y zhtqggWr9w+=+Mt8f1dQvFIEZmh zU4%@>Nj;dF(zu-{=FLDdEF}RHL)I(xE-H3`|D1#sy;TSbkc3!jVx@d$ijO1>56Wu< zS(%EuproB*m>d_9R6?y)Qw0eGcWJzZD^o%vD$b}E#~PGkDU(T&EA&3hNtkH)5XkzKag#jopO0l?U;70l~?MJ zBvF#3WOB?^MwB=mg*$<4ywJ7Fec)cl6Iza*5-BP{MJkEWIFc$``G%8Goje0V1%q}w z&-4nRrs9McFr3Kl6qS49KqELT!tvwDT`xXi`TU}L;Q2%Ry;V5Gmyh&u9&hSw;%)0! z9py7Ui$@;!n##se{Fg`hLhG4he7whduleNq`sKTPfA3j(SNdA-vpu+-%Y2;MSn>VG z@+FtQ;7W1EOKd{m5lgC20$_sr<9&b*#`p4^? z7x^5I_XXehM0EWZ$N2`p|MS?Le>`%d>Nzj=h1`!G?<;z)cDL;IF-xDB$2}hb%)`}l zeK4)|y1m`LS1fKi=fI1$lsqqSzxyW$mYi~e527wN-CoY%s~5T--w6S|56_B@TfaQC z_lm23?{*iV$bTXfoq6Hw?#g}qgmw3G=dVaxE}qvRRJP!L{zTv2E$W?@&Am~&-F@mr zAH=anqYoY5yY2+HwH2D)b`mt*32vTU!|!$6k3*QrjqZV>g(H4^y*K*6@h)*R6y3AO zx9ubbc)?lJ58U6NemfIw1x9ZbsIW26wP#FKGiby8(;gqTc-(`3I&1IUe>~Cs+%_MC z_+If`)C%aGR2Q9?$1w#n?K^Xqwnxt_QFrPZ~G5;Iyax<+qQ>6p-YvSv9E)kH=NN4VELYLh?ANQ zdd->wq~7l1r@)wYKeYCN{oZ)gJ@W%F^4?3JXz?>^-&v-9a)x^)1eDz0&5BODROYzI*itp^bO%tim^6ebrg#-}I1svy=Pp8*v-ov;38-kW`s z`}D6M{D%7G(KUJQjJc1^d0xHHTsUR<^lf*}|6u-#`JDH)-m@2exB1jf4{tel^Y74@$3Y`JUu8|QZ{ zUFE%V=_lKR%TF(Vdbv0En}y#koU*mE<*tS1)_sdVn*YJ#`+x=cnLh_!kUGQt*eiXT z-2it~$i3=;m2*Aqbtt%X-u)>LCErRM6?VT)f-mA+;i%2-(_e%|;tt*S%Wb=7+~-Sk zpP1Wm{r07$+SbPx-?YWv%z5u#D9-z!_zIS?QY5wom|GDSu0ruy?-*i9td@IC+o0$%L zr@$}%W#w0%=YR3T0d)6^q3?%_pL|8{)Q!dCw?kMwUO4vMlzHGWrj(u0|cm?+t zR|wqJD6L_4Th!eed&lKee;`!u9S2zAHT5>w?tM z`pye|SSbDJ^J> zqgu8vvSJnq`*Y1PE{0oLPc-yWxH?WofD3Jcf%aoJn-TPP_B++65Y;N zI5XA#iB3ihQ)U%2WB#bEQvp@eFvmY_G`o=$lk95_WI?3sR*_G$V}a-~>$-(h4#RhwG5Q zo=9VOE1rx_ryZ+b8oO^hex-e2ck0TU-tRjM8aUL>xW5JspwEB?5?Lfb<3`SJ#x=Ps za8+OsBm(0kPxU8ySggn6f?etcvT7`mq$@)bh507DINrObdDolS)0Ms|j4DnoV{I zErpIlaS{ZrrU~8wC8e|zMx6%NsfEH3BApDG(MglSd(OB^3K?E#!e!+2kne_?!7fuT zc8y*)6=o8t!3a$?%1zT5B#|M4Q2u-lX$(Mp5wa)5Mk$jpWIWBHSbqq~#I0mO>QjR_ zUt@A~wHh{Snux`~#QUi4aQefcp45LGV10H{rxP|Ys3hxThE|8e_EgK)h+$)lcget1 z93nj1YjjFjISVN0q1oerPecI0982XjAsRHvkQgTD3H2+vDH*Sn3N*AxaC|=$ZO2O! zL2Bm)H4Dfaro2-a&{8GVYjj)DpxqH5S;OR{w7)oZnr(TYhg&^i<_j+66q2{1l-N*> z1|+ZzFqw%38*FBn4tLaSBS1s4d#T7L@}rEH$;oPmsDSLRnJGK{RMXKXWL|Z!sOYZ` z8m8&D%#LU!AyC^Yh*P{Vr4nhb*cQg|aj()0+nsnY%4v!rqmzyk==LX#KrmSxQry^m z>MB5rKR&nP$2)c{e{4zEwy^k~Ezah!_p1vj&+q0QpL-YBOoR2ef5CUO$9vBB9cQm! ze~ZuS0d?#fUjG-g_wgJYv3`H65A%5c{nXrV*Uz}kH}ow2YyF;Q&N~<12B)fDe$n?6 z@6D&3b?f>AU-AJg{56B2Yei3 zH+P<~bJLCocYJck8+W{N2eo7S_D8(0+y2?@Z`*EdKYRNz%a1MJvg|I~%c14tmVUkT zm8ENzoTcQ_DckWY{BfRNd%ohi#^ZQWVCp{o+`;|1fA=or z=TQnIv}lt>6p&j6y(ATMsz79VLx&4D3{s~I1Cs0K<`;1HghzbR-cuLyo`W&_&U3kM z_^#i>;5un&48j;VScwXlX&@s>BL=`^K;_CHX%IDbr$6*vd*It&+v}Zm?rzWQ+T%X( zP2a_9te>Gw-2~OAnJyO#3QMX2YSJJ;MKK_tMfyRTcGl#-2T^VeRRftS4UA@UCSygQ zAb7>Hs);Bpsu+q+u!gD5-8!p@W1ybJ7{~h&3@geTdTQ&+9{20_`EFQabR9+x zW;h9IUm#QkQd*dSfub47f>BPw*2pZ+?t=u8G7==S5Q6||G|;F46>!wl5RFg_)Bwmn z2-nPAGVA!5B;5(c!PS&(I; z;R%7p7=|h!M$VGz+Ko$zEMV(EB@Q(AP)vhG!o&^LWDUclHo1%6_sOT~x~a~-QxaV2puR>z-x>>Z zLoqZU`!wK7n)}pj$nMWKt?W7N!r5J+6l!Q7ln4_X6y88k4L%2u<3lKdHVl;pT}B<9 zd$s$m@B8+-Z-7^~w$TVaI#8rz5mce!N+f)yDh*<92FX%1XmNm&xiWXdHcxUcGWU(; zxj%V+>$%%=?b3%m7kIMs@%i^JL>7+s9=oNq@YKSW7vAT!H=Vob9h)BAeADJjH|v{U zwgsL0hUb)>-`;uS{0W=Czw>oF&tFV!+O-qkRNJ|2$J!5fe0s-aJIoztZF$Y&OXi0= zj#=n#e{9Rs+i&&Wvi;qgJ~w~ImixSc?Vaty_7^Yz`|@4h$Cf|fb(g2h>E%;5AG!4B zrEksu+rpD`f84U@J$Cb@TQA>qU~7A8c=Jv3v8~4~{@U}~#jh-0#u|QudX@Xs zBfh=fEsFrb*ngjIf8zh&%F5Enfgj$v*S+XIcs=iZ#JA5gf8ecm?{$xV7=Bzh@ba(i zb^8zda`OvU{^u2be`xj^m6;eMtQrVq&U7DH&^P-#57PJ@e?L9cMUv)*&gvB z_lh6+_9YG`uYjF(u3-f17o>uiI0e&|(O`~X8i|;i$?BlG2+OqM9{FQm;h7I*a`(!f zg}JwEa??Nb?S0q1zI~fE&7SXpi~f1frfswS-0yj2HU56H`{y4#ySQn7MPW#KrnE?a zexeS#Zw$&3h^CvQP8+azoBG^q+$X>8E1Z1LyC&k^_9NfP-Yp-VJJtQ;HrO=2^mX_S zKlD@IzSCzn1gipy!43o;FpLNm0YAc+p!28`IHAEOp{a`5Sajyx8+Q9n_P}xKr@p^) zUv)3M|G7EX#ou=S>d6K837)Lq)D?w748OUdqzNuzOxF++v}{0cQPE(v-8{SC!DKw3 za71|6ebaX!)8W^T`u3j+a+0th;qZAf%xseY@k!Xr5m2WiRKEgmL8Z=wCTF`R{LFW% z+xwaCbdb!X8F*Qsf(N65tr|uJL&Fu7pf!vJLjJFLdD2|yT5SqtnKmcxj*;4WPa{9?%N((-RJ)3 zA=pKJ@N?fLcgJJCe3W6}z`@|K3BrxS`bfiJ2#y=1Uxfz-LzqnjP+MU@qy~QB$HC>S z5fpgi;V;;}L1Gi;8Dr9O|LT6^G2edI`#8M2!sEW_$tq}^f(yq)a0Yfyn2yj2jzEYX z1`hvXI8EsoUgESwM!3Klik9^r$5+j2X<@Ll9Kp7338 z670mYd;Ndp@V(%G3ybG<)d-|O*-=d=^22V_1_|xHNEQh>+a_7X9I1+6%Gt@Jp3*Wl z;MA)^cPdIP-RR3**w6h*z%b*w!S)J5KN#j}Vz%mxr}>o57n+S}d2K)ude~^S^1UI5 zTF}jQic<@0B@mO-5@PXYz~n&i6{x<+M!gobTAZL*XlasZ%b7OLq>WxDz&b{EP{4Sg zkf(+;;`BR}QM+AJ^1U)Q$ukh`F~W2Wks_VC7E0w*2%Ki+bQ`=(Cmv)>B#vZYJB&W{mK>JJM|T8ap{AP z{PKvzIWKlUKffxy^D!u)s;?cy_r3m1_vU|Im0ofB#ao{X;29>c`f7@b0J9u?qhP+k zHV;b>toEu-`WeM!)YC6se6ISzm=5ke9=PHW`H$3n50&;qh9E?qit|wPvPZ6bZ}$Fk z0eLyUvbIS5$M}!&-#mc0D+}7tcR;R|j*{bE96k zg`L-Y{=>T-`Ocnq{c0EOE_r<`?!B{OzxwrS!f!15+&BF_+^oE51zn`3qs!x=W!t^C z2t^OgijKbQSA!R7yU%o;OJ=EiuX^tC2Xj<%-@X}oSQIYJExDDUd)E*;UEKmj`+uG9 zbN@2F+D&a)xo~mIpFZ~0r89o}I`^|9sIX@dDv)=);q9?R*$=69Q1qc$5&QkCUz^_a z@bT`;XVGJH>k1_N9mLVgeZ^L2y!+9w?Aw3TOq$-ng_P z?=ivrWM?wgfPsd;7!Z7h69m|Cs)FOXN@;MOclRwrJM_`$)4_@LC-*KAy876xh}r+u zZ%F^}_^;i^e+w6+wnGuQYJVx3uRiSB+u_li`Qz6(c`kjui_y@A-vJe7J|g+M?82xA{D<>h4sE{6lgF*>{!an4XWg;oc(B464zMVXBO%7hM!7Pci1{PMa!HjV zC{3{;nZ??z=tN^i7|F2U;xA5`z{{l;@mi6{#qC5x4vrHd*XmCr(NqatWIveRl^jJbxWG%^C%^*G1P@Yz-wK_{n3ZBy)rd==xPM;Zes`*?sE94VQ znVn!njrCLUU|#iC3?tSVP88Ix_$$#$nPQm`V1S01kt~!Fv0#uW`YnfwR`@;|bDU&Y zj1B@k9#o|v#h48mkIQH~(9MY&J_RUGgG}@r0a?X*Oxj8mt6Z$W2dG|XMx`gI!$}Dx z*yf~c4lr&c2M0-kwkvPgX8>wIuFquk33#e*DOjXHLEkA} zLq_z(m0x+=*PrHFKlh}Sif4JYApOO;c@orkY|qMl-Z!|PKF7W7&FU@{5oB_Vv$qx$}vYm$~ zR<1mHPyYEh{Ja@ucBFl|&B4O7`7mkBibrmO{} zARes~WPM1HnXaQ(vr~o4sKt^*F#e{0iYd)j-VxAgN?q zeie(e;o$(XjIcr`6>d6cpOVr+yu+nHrU55SP*6zbgZW$rGLz3)IUHm>)Imn+Z|`aX z?Qm^{)B81`(~J4JLXwf<5jxu?&2nSH^Z{8hXilfY@xa1FEe?d#5h0K!dxKHh-%g-S zc9MqNd`hVKAw9CI4Hy{%dMAlTD&hVZA1Y-dlqgkfr-5|vNTkqhmHbQ>Pi6v|ZH(IX zTDqf(LcUhX<;SrKZSmc#n9fUn3<#zmen6r_x)YTJkU;45Nj;PoM^cS4Z4Fftb}cOz z%Uw$C)FS;b8>@E%N}*I_t3@;%VAAmhTPa6uG+6`Y&RoBU;Nx1mso*WG&VyL-ppa~} z^9eZe-9EZ_1_;=$)z{!oLKkLxQZc)TCKs>Am2U&u_eQ&rm*l z+^cQapxO4yUfA@myAmt7SMOfA;-=2ZVHC}wUJEY#FV=zrS;dnjLxcQ!F-XK4K%&_$ zs$<3G6J(tegGF7o^F1wI!vayfUW$RcvlT1nLQ-8Sr&~aqY_tbL#9|97n~GWzXfg3) zBOx?`Q7sTjjQFUc$#ye@laZ1$A+sY6_?XQI(kul7ZHrmsWXkEsJ0u7L<)(6hPADOn z3#eI!Q^jaAnq>vx#~lRGEY4*cJ+{TTKXb!TBYK1Hk1@VaC zw2>=yL4sw_8IJ6XmTR}`Z4O5^Ra-M3jmY*7?q~ z6U^GVQej+>sC=!gxA;bw-(W2m6O1$*_vjWI(wT`l8X)C~0*pEe%`}bvs2(ossWF}> zIgT5{7G3W&Y%n6pShf_*aYD11tJa-_C3Gewr_t(7z`w~!SPUlhWrrvAA)nM29`iSS z5&B34{vr+M0&=Aopvb1$5mN)(acFqyK)4~rrHKeyzx-HG8-X#yFlvn;MnGA?R?P~B zCk&x(;LTKBYS$)KB^<_vc?)S2WBzorAC;pDL0DNh)F>ioTIr!|nvAqYB~>jJBv8{S zt#uOhBoWQG=~S&KPmqw`%uEY)e~rwf+foJ__oY<6iJ3~SD`CZ;5=cXyPjCXv`_l|b zpeevT1_M-`t~upmpO2>SL|+|f#iG#4M`DzW=p#8%FzrI1q_<;zwpo>WR6bx9>0US! zvAU^ZrxO`jMI^?DhqZENlmdCyW*AQr`F0ol;D|YuF+3aU$EGDVQ7CC>FgLZVGCKuH zV<59`<{2hbY>2rD8YmAEV@f0Xj5Zcq>rY;_a+pu%P@g_-{HxOk_6&x%o&mAD$}muG z#8ZUd@@I`A93?^}M(I+`AYV#_vh`ss>?8yGrF7^HI%r)2n@q3DNcYm}HcPNb)5q&o@Xh*-Azg=t}u8Vr?sdaXark*pvE zBVtbzOL`=&WU*4SAabnM?hX}kjOziUisq(G8TgZe43?xDN~|8oINSo@hY8<&JrS zxp2XcE(B}WE^n}Q;AN_j^chVqi$R%&c1kSkE-&cqDaVpOKvLXejW zMLW_Q7wKGwtK(fKSRMtth+oSmv5E=#sKh8N`kg|HNiy|OKB7um2JbWm2*%ce!)(pz z;$X__@t$h4U9fBBWARMIDEGvGV1+1~LNZ-Z z9!i-(rfcU~6-}n7A(qIQh1?*dR6B#bPzzHr4ZJihK8-LEQQEeXoRVkyR$G&CP#bKg^{E`uQg*?jnJ`)vGK8O3E08vpK!R9| zMo?R(O&)Dk{ljpaYw3($w7OX%Aa(I+)h`&6hD;2LEE5NuU_hk<JeB63%LI#gC12Iu?yii#AW^6S@>PLU2V#u&2pHJ&I1^6Dgq=lEA`~01WTv zSA3qKyX6Zj{*{NlpY)!-aP7iq{>gc3p7ngfbK2Y==Z0WkpX?5x!WLuo5y*<(=6>Nk z2-*DM3oFYffp38zND5JA00{UAAe;&y#~2-C>u}U$03YJ+y9GjWdw&}3zw|@o7WY~Q zz#ku<6)k?{!uKtwRzBqJ-r|$aIQ`<&o{RUO2Z7phtG7ZIKvN5!$Vi@ffUOAv}X?KUVnmj2W4Pm;8^ zxi6}(O7!U$pZt8U2Z58jc>dx&8(E$|yKQCf7Gvqa+@+%{-19Q<46H9g=ckck^UPO$ z6?L!v;!63XgMc}?Fa8oVTlx6Y*Pd{?ev!M^3%7agtZ4uGw?DyEzx;OhmD{1{^e?Y0 z?Kv38rhDF(XN@2E-I3h2Z@JJlXTyGYRJpdT{Z}FDy<+npS zntU*lh*d9l@4X#HWU_#K0&I`Y0%Vv_4L}i6fF)85lz|dIZBl+$x&xYRZ9nz0?Nln` zo;FK;yl+;7Jn@<0xf1mecgLM@2L}bydefcI?j@V9zKXi~?;dimJr*AGQD1?gz2~QY z!2a~wZ@4dd0TjLaE3=1l5P(zn_F3bz-*gUEyzJ(OT+dxF$>uwo?_a2WV846G?0=ho z`sAm#-tZ#J#b&v{FaIi3rwU()Bj`fB&MyP?eqzec=g zykY6gQC6fIOA6@B_I5Z%l;lRIqzA)Jj?bw zUiaPSeMV>e1iVtOx)=OeOY|$QfBUoLZs+J#&i&rk!JRdGeCzjqW9ErUDgU;%{_}4_ zXvaHy5^myKEC1$=$6xd4dhpxOv3L6|!S!|L{*{quaVdS)_h)^%U%4M*H)no#Wvj>Q ztw}Fg7av#|Lay{(_uapDzEw&$p zTfFi6E3ZHOaIWGZ%vG$qzudn1pUsiZt569w~9veBMr)Bdkr)l~J5ZPc5b2 ztT+1=w3ZlZrLj<~ReNj}SP1JQTJAQn6jrL>IL;$x9FB~A!U>_E)L03}+NN5Gk~O)c zwW_^DNYKW~Mjvfwg&Kx|C%YdhG?ES3Vxpxr%gUD%bxqgo5t$FgB7og7rl13sQ8-*p zW4%UR;fhWk%!g8@(rra}qE|~OmBx_tw<_eYXJ#|96;IiKVoCWei7nPCJvlr28cUa=a%9q(u=$z>Xdjl3Bpqj5bm+D$@hnYFgQ?hH&>Ms>AcPAIYSXUM z7I4Hbg$OEJYXi8c?(cUCC8^jMrwd9?PM6STuS+=@zEhacjbxV7h!7)<$!u6d-M1dU zV&8PcYGl*ld3^}y_3O7=`9w~mq+FUx1Pd;blJ;Y8k)k1`BMV#P(9}b7pSZDyY zNFkRpGr_z(X@-GGsV>B#!7f3K29Rbko@AUZl0>SVL@s88W=a_qz}G~3X`2%IN=ELC z>jaN+3A!DQnG#a(;dwDQ3P-A0izziNFnkMK!>A)nVhz+h<5QC6L|d3;uIBARDi8#A z)to>%QbhoUr#7M^M*wRXNp zpkX*p){|^0Ka9~-flTD(R3&Nxe4vy_4e3BNALJVSR7Xy#J%J`0fQ#T`m0pK0=UJ02 zn0c*&GxFL-=XvzJIGzf=*O2kiv$n032Dk2pn zqN(cob*rn7FC3oF|I_oCZ}ECQHqK&P-QpVgKpbIWBC1+a7&IAU$>CH?4hAJF*#P)R zDPO4%#ZJ8}_#3IAg~f#=UjecgaAq>~v6fGZW*Y4jjCdTP;bEoNv|6EPs?GB3INXkQ zP&NgEGI8MXGHUXGA@b-%SwpIjN()@vpnOD@24k~gH@ir&(M4LJG~JkFns_~k4ZD!S zOT;UR$d~%2)Q=h}q+8G(oYx0vK5a;Jb<_n=PAU&MkO5SQX4(*8Zy^IMrj0RW!e=#h z%-{iPDui{^PK->fKrrDc&~zh(fE;cZ{%~NBZAH>{Of`dD&{->{OR8Xn`(`6KMTbch zX-ShPS!Yl_5s*R?GgmJT0yTuoVYJxq6p~$*4dn2s7|uo~0?vT7e*Eg;VECbSLGAzB z=Z>gQb;=X}c$$jEC0goC0{v9Ks*m&K+LX0yNvm|M9u}k$@$|SEuB*LHl6NX>KH5xS zp{c(SsjwPe0iIPned?QYiTq)iG)$(&A- z4UlzQngBw+p3V5-UMw}BJDG`rY2|(%sU-5P7E91ogCADpkuY>xfoV~cnNCjZCJc-& zrlym0DI^y~I+CUR`9yD2LVHrWgGG>`zn#W3DI4pj`~6BSq36o65yTllB&u0W5LkO` zC;MqW;im?Tx-g=Wx`7CJotw}?AzuTKp5-$S91(v0y#B-^ z5J>(iz4}e>%;t5kWma$Vo_0m<&+9FARrYMyst~<6y2rEl(wobjjW%yRdlh&~&RIRovwEn%$-kzgeAX5K zM`yE%hoxYwfD#R}pE5F>B*k-9I-MS1!(dEMEy!gDIWS6BWT~q};Hcn zJ9&9%tDL3>P*8&KD_~CGcqhZ-fdn;awGAeX$;o&Y#M3kpVs+63k_g7CCA?il0QVuU zi4#1QlMQ1>B)u@r6bR7UDsbdBz0I`yH>w6c*>MCG`Cy@wVhE8@f@5eUf|j#fm% zMZemOst{bM2P;{tH58&5aoCQb)4F800rXYq%(VRm@oc)*mjlgdg-!PZcD+_o;>DcG zclmnR1*TuxY8Yv z)KHQQsmhi*`Ha-2uwu!d8CFE9Uz!0_R^`Knz@dZ^{!K$5U9}1w0aC%j6)~(0AV!u4 z8Igfsv>jxu;tC&DD#=N*m98boh$DAmPCW;kOr0SDAiFm-#@JA@2hDPm2HG7Y-?aQm z0#RyGxSmfcL_UlUv_2ja0(L_;3Ve&9EZT3Am}NCb4V$snTJ1u)XUiogL>FZ{J}zp7 z$*6-cd0?SbIjKb{vYC%D9X*xqq+&p=Fo@I;e^nVIJ2H|*s65B?bCpVES|5yN>};LE zQ0$=zD~)FGN-0n`RH4#~6bncQ4U1?4@P~d-shl#!N}R?^RXNU)Rh>wsv|NxL&@9_6 zYEf-K1I~o%q-(Vn-|NLvhyy2oli`4CXUb}=aJgpZ{q4Nn>JSa5SMjH(Y-t>7O++V& zn(4tvF}U^XGv=9^vA?kF)cNwup%Skw9vR+4dX z*z{9klvl@$_0+$~|*dDGUrAczEIfD$W z+)S@T$IHDo({ADv!Pa7#B-kuvp;&~lTBtbB?243_=mq0L#@%T7uAOl>}w?r)fGF?2Ma{T&)m^YQr`a>e&$jpQP8HsvKUQ{}<-7 zq*H#BYfQ4qYJ<*c1u@vhfLb)m5{*6$5Yl*~73}~pCfq@rT(3H{bi-^Ck!)hb^a3=R z8G%VW9;o1stfdnTwb*5P^%NzV-AvFA)5@P}WEsAYvC6eXv+9hLP{-2k;vg^klVUGh zTpNd`WVqC@Lt0ad2Kl}c0R3P)8&gWX6imei($uXoM$yqoBnBa9#lg}_zSg#J09%I; z8?Q5CaT@WD11O$OOTZ7?;EZTjm2tD9PV-Gj+#oW-prnc#tCumcTJ2_q{s0|MR6?9k zdD+a+7KfC}7}xD8Kst}sxhbA;com5nF|I1r(J4FS*$GL6BTXCTbWramL;>`=Of%Wf zq(l8^m9Bw&OF9bla4ZoB=CuNq1xJl_czym~n9q?(Ml1Ee{*2`HNF!c|%5`y4FBp0v ziRU708z7tEQ8Qbu62KeHsdeD=m+FJsh^!7xnHUdDO*i~DZ#Y6CiX~WG69y54uce~N z5;`p9gi$h&IyIr5)Y=)O&{D8gWm*W5-3+kN4DG^N-|D5KRlC{lBY{CH?++uLGL7(L zn@EM7E)S@TL75WLVyP-d*s6{>sX~@)rR`b>G>FY!8=ukuv}T5*@F)`Wdps)H=Yf$(ytlCBoo`4K4!a%9|AqCJ3pHl-GpY({J<-cTX; z32fT-;r01Y&gaqU-#l*!X{D^xwA>I7l%sK_JRpMIv}6~%R8;5y0fQ4lWTGgG!Emqtgu{O5Q^fVI&_=RAzYe?W!$$Y9PXQ8m@U zrBTn6TCH%86&;#4dCT96RBD7lWd~gjpP+ds->uaYn~Rufl1z&%CPsvY+_KPYw@D(R zGwrn7VqR8I3y0yUM2WLxO#y*=ZQv<2oZ32g5G$CY0+9Z!fGj$z~G2wcYu_j8~02Hr4g5=bcCqz#P^GjL|{}rfPei)z( zZ=V(IUHsGuZ10_4cJG}P9s7pWr9IDbr~N;v)7;1#pz%&Vw|Ce153=sXZ&+PhJngza z-+0!G{(Pys`_rp4%+OiSfpHFoq0W8X8==FM6SL>;*0_>;^BbW<)^pUKl6NO=ckjJ* zqr;az*P(&=f$9+`+E_mcObRp$>?(vZBgJAUU`_&_7Zi7IeG_zum|L>vyye^5-HYb| zx+1PwM|qZJkLS7KTcFrI^F6EAd6$;|X>t80?}7fkvjMNi z-#7Dk+qZ1_`m=RUbWgxm&t3n>RjXGVcHkX42i{*FK+4ZV@~u=di97AGk?N0$hBX~I z!%17Hqb1C02V>EA3ODhNY|))z3+)H+tkDFP838n216{&NR?~JOV8yz0I2ZRf6uQ)l z_OjtJD-U9rVNMzZ!^7Ev!t2u#9?V3dXfHdkTMVI;l9{T350JHnla?r? zemZHUMy*z$oHFQvQm>4-N>k~HBh)Al<#NP~4(dpTp0L9@r2&yvsSHukY`NVMFov&i zakM|;8>31*j%P(WOz9k-Y4X)lDqs|Xjdr?^lR%|W?KxN@90mFSxy!~oCY2ULfEu7x z&ItsEjD}1*@uU+mtx(kAhVio1PzQuPO1DPCTCXU@;xRmo2&q6EczT)*L}dLSduvYf zVx~K`F;Ncr$LrVrsr-f*>K0#1vL^ugi?00P;Aw|+PE?)0u3Z<~s zkj;J@ej-6x#~NDwQ@4_J6ICu~PkFkkIgEXNqOzaE|X*G|jY{;aIH;yo`f*w9rih zirE;Kb5c`kG=|-r#rIXA5UG|_EicE^fLzLCX<4Z1c2^0ca`Av9`0XH{=z{WDfEIhn z#t6-+c5#$pnsK()?b(SkQYuzSyryzWj;u+@v|S~@RYcqTa1^nFD60t5F$7pXx?0?I z7@2a&kSYJg`V2~@Lb;7eNu8 zb@-Uc42Py;*lWYO(THelhe2$A%B(P@ZofbTMbKAF)2t+h^IWLhBYGS#8yao37zky1 zXeF9!Y4M;*v_m>7q6`iMFcXj?YhdMuA?Ugqa9VlG>?bQup%fSWy(o+J>&h^R))=cc z6zlzAvSR0Debkj35pYdT$AN$~iHJ6mM$LZ5Q9H6=^~yb|;-rmgGE$*aRiW5z=rO0} z@6dh`h3&Rt_Yw)Ml{LbJUJuh5 zGDJ3=V9Vgj+{l(hn=5OGfaHKGo$aWJ@(536BgjCI*)A}zHMz9d%1y9(4qXdm!9`Fd zBDlXB91WR?jaqpj&0^g?U1(*Act%q*nyF5*ZEVVm&4J2egFsixbgM*9vO9i#Dq2+4 z&hyQ7uqzGqk=ReNkzgsB9ol6}t@gkxQJKbgpq3yb7VylcX&uz*l|r#ulKeH8DWiPR^0zCUfGyY`3~AQFOf(xGfKg@nM3f-~w^%e0!^K8j;b%(l|pxD{g(qo6}- z#jNUZdO3#M9nwM^5|VdaSf7tk&sa3)W=yynEV$-Q92jtauw#A4r91Q;FWqtE_MdLQ zdHXxI*SDX$eP#KH<=d98T<$H4%f~PM`v&d#WP|qnk3f6=p=}p$D{kArZPV5Vwtiyk z8-Vhh+`7E@$c*s(14pJ7pP^vA>XI2(@hvlsP4gfU4CbzIFTH;?v%%$jFq~#xck6dn z&saMMdu?w08RFCbpA4xRqoRSh)A-KnVE@Md4kF2B?#9`jxacDAz+mc3<$O)sx)V1FNq)SSHpB+|Glm^4^0mr{_L9yCe6pc^JfF z53Zta^ew9=oi_K$+0YJ(zt#QR_g0^NhN|^--(UT^jW-U=*u;Ow)_U|!KUftGn}Gl0 zM`ob@tVC70+r#qpV56?c0$UX9F;j|WOj%0JhG`@^-BF(!F)hnV zw2atV+u{?lzdy<(4IX2Le0o5zoWR%pgurwPWTgwMZV;P{`Xeo5H-dnh8h3#n)S@$F ztY?+mMXC@>0U1H3FNVsA9tcHqn4wHLKls`LbS7J*_*m5-iG+k_ll@{^)-=B4*GfaN zHDsK0QNt!&tv#Wq;btS=NM|7mX*&6q9@aPwwj(Fe8-^?4>QGA5b9%j!uMEUNW8#lQ z%!wlr<1iN?0Jy71P|9W|1)UrV23fF6>mPh{^@+n4;Q#n^j+I*^nKA}wf;VWbNg4J8 z(ytSBy<32JTdAiSdfX{C(5X$0aRn{eML%Y=fflzK%r*o^4CaGvF*t0IWzbG&Qq8nU z13X_YrI~&!XDe+9#p*ypF-3aRRK?Xc-wF(;X*4bZjHnQZgO+F%;gO+PO@OX#KVna( zyi80W+T57f#i~~GSCg>-PsMU%8BDKUb)c delta 70213 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 diff --git a/test/test_api_usage.py b/test/test_api_usage.py index dd0094b8d..6c9dabf6a 100644 --- a/test/test_api_usage.py +++ b/test/test_api_usage.py @@ -3852,6 +3852,7 @@ class FakeBuildTrigger(BuildTriggerHandler): prepared.subdirectory = 'subdir' prepared.metadata = {'foo': 'bar'} prepared.is_manual = True + prepared.context = '/' return prepared def get_repository_url(self): @@ -4046,7 +4047,11 @@ class TestBuildTriggers(ApiTestCase): trigger_uuid=trigger.uuid), data={'somevalue': 'meh'}) - self.assertEquals({'status': 'success', 'subdir': ['/sometoken', '/foo', '/bar', '/meh']}, + self.assertEquals({'status': 'success', 'dockerfile_paths': ['/sometoken', '/foo', '/bar', '/meh'], + 'contextMap': {'/bar': ['/'], + '/foo': ['/'], + '/meh': ['/'], + '/sometoken': ['/']}}, subdir_json) # Activate the trigger. diff --git a/tools/migratebranchregex.py b/tools/migratebranchregex.py index 832cf2bf2..791d2b477 100644 --- a/tools/migratebranchregex.py +++ b/tools/migratebranchregex.py @@ -5,6 +5,7 @@ import json from app import app from data import model from data.database import RepositoryBuildTrigger, configure +from data.model.build import update_build_trigger configure(app.config) @@ -40,8 +41,7 @@ def run_branchregex_migration(): config['branchtag_regex'] = new_regex logger.debug("Updating to branchtag regex '%s'", new_regex) - trigger.config = json.dumps(config) - trigger.save() + update_build_trigger(trigger, config) if __name__ == "__main__": logging.basicConfig(level=logging.DEBUG)