diff --git a/src/containerapp/HISTORY.rst b/src/containerapp/HISTORY.rst index b5b55085ee3..ad688317c31 100644 --- a/src/containerapp/HISTORY.rst +++ b/src/containerapp/HISTORY.rst @@ -5,6 +5,9 @@ Release History Upcoming ++++++ + +0.3.34 +++++++ * 'az containerapp job execution show/list': improve table output format * 'az containerapp create/update': --yaml support properties for api-version 2023-04-01-preview (e.g. subPath, mountOptions) * 'az containerapp service': add support for creation and deletion of kafka @@ -13,6 +16,7 @@ Upcoming * Add regex to fix validation for containerapp name * Add 'az containerapp ingress cors' for CORS support * 'az container app env create/update': support --enable-mtls parameter +* 'az containerapp up': fix issue where --repo throws KeyError 0.3.33 ++++++ diff --git a/src/containerapp/azext_containerapp/_clients.py b/src/containerapp/azext_containerapp/_clients.py index 6300c88f75a..e2642b97303 100644 --- a/src/containerapp/azext_containerapp/_clients.py +++ b/src/containerapp/azext_containerapp/_clients.py @@ -1226,6 +1226,34 @@ def delete(cls, cmd, resource_group_name, name, headers, no_wait=False): logger.warning('Containerapp github action successfully deleted') return + @classmethod + def get_workflow_name(cls, cmd, repo, branch_name, container_app_name, token): + # Fetch files in the .github/workflows folder using the GitHub API + # https://docs.github.com/en/rest/repos/contents#get-repository-content + workflows_folder_name = ".github/workflows" + url_fmt = "{}/repos/{}/contents/{}?ref={}" + request_url = url_fmt.format( + "https://api.github.com", + repo, + workflows_folder_name, + branch_name) + + import re + try: + headers = ["Authorization=Bearer {}".format(token)] + r = send_raw_request(cmd.cli_ctx, "GET", request_url, headers=headers) + if r.status_code == 200: + r_json = r.json() + + # See if any workflow file matches the expected naming pattern (including either .yml or .yaml) + workflow_file = [x for x in r_json if x["name"].startswith(container_app_name) and re.match(r'.*AutoDeployTrigger.*\.y.?ml', x["name"])] + if len(workflow_file) == 1: + return workflow_file[0]["name"].replace(".yaml", "").replace(".yml", "") + except: # pylint: disable=bare-except + pass + + return None + class DaprComponentClient(): @classmethod diff --git a/src/containerapp/azext_containerapp/_params.py b/src/containerapp/azext_containerapp/_params.py index 2cbe101d496..05f5b7d8068 100644 --- a/src/containerapp/azext_containerapp/_params.py +++ b/src/containerapp/azext_containerapp/_params.py @@ -255,6 +255,7 @@ def load_arguments(self, _): c.argument('service_principal_client_secret', help='The service principal client secret.') c.argument('service_principal_tenant_id', help='The service principal tenant ID.') c.argument('image', options_list=['--image', '-i'], help="Container image name that the Github Action should use. Defaults to the Container App name.") + c.ignore('trigger_existing_workflow') with self.argument_context('containerapp github-action delete') as c: c.argument('token', help='A Personal Access Token with write access to the specified repository. For more information: https://help.github.com/en/github/authenticating-to-github/creating-a-personal-access-token-for-the-command-line') diff --git a/src/containerapp/azext_containerapp/_up_utils.py b/src/containerapp/azext_containerapp/_up_utils.py index 69cd5df1104..8a984251efc 100644 --- a/src/containerapp/azext_containerapp/_up_utils.py +++ b/src/containerapp/azext_containerapp/_up_utils.py @@ -45,7 +45,6 @@ repo_url_to_name, get_container_app_if_exists, get_containerapps_job_if_exists, - trigger_workflow, _ensure_location_allowed, register_provider_if_needed, validate_environment_location, @@ -1041,14 +1040,6 @@ def _create_github_action( service_principal_tenant_id, ) = sp - # need to trigger the workflow manually if it already exists (performing an update) - try: - action = GitHubActionClient.show(cmd=app.cmd, resource_group_name=app.resource_group.name, name=app.name) - if action: - trigger_workflow(token, repo, action["properties"]["githubActionConfiguration"]["workflowName"], branch) - except: # pylint: disable=bare-except - pass - create_or_update_github_action( cmd=app.cmd, name=app.name, @@ -1065,6 +1056,7 @@ def _create_github_action( service_principal_tenant_id=service_principal_tenant_id, image=app.image, context_path=context_path, + trigger_existing_workflow=True, ) diff --git a/src/containerapp/azext_containerapp/custom.py b/src/containerapp/azext_containerapp/custom.py index 681919139d6..6d5a5fb4b7d 100644 --- a/src/containerapp/azext_containerapp/custom.py +++ b/src/containerapp/azext_containerapp/custom.py @@ -92,7 +92,7 @@ check_managed_cert_name_availability, prepare_managed_certificate_envelop, get_default_workload_profile_name_from_env, get_default_workload_profiles, ensure_workload_profile_supported, _generate_secret_volume_name, parse_service_bindings, get_linker_client, check_unique_bindings, - get_current_mariner_tags, patchable_check, get_pack_exec_path, is_docker_running, AppType) + get_current_mariner_tags, patchable_check, get_pack_exec_path, is_docker_running, trigger_workflow, AppType) from ._validators import validate_create, validate_revision_suffix from ._ssh_utils import (SSH_DEFAULT_ENCODING, WebSocketConnection, read_ssh, get_stdin_writer, SSH_CTRL_C_MSG, SSH_BACKUP_ENCODING) @@ -2765,6 +2765,7 @@ def create_or_update_github_action(cmd, service_principal_client_id=None, service_principal_client_secret=None, service_principal_tenant_id=None, + trigger_existing_workflow=False, no_wait=False): if not token and not login_with_github: raise_missing_token_suggestion() @@ -2789,6 +2790,16 @@ def create_or_update_github_action(cmd, raise RequiredArgumentMissingError('Service principal client ID, secret and tenant ID are required to add github actions for the first time. Please create one using the command \"az ad sp create-for-rbac --name {{name}} --role contributor --scopes /subscriptions/{{subscription}}/resourceGroups/{{resourceGroup}} --sdk-auth\"') from ex source_control_info = SourceControlModel + # Need to trigger the workflow manually if it already exists (performing an update) + try: + workflow_name = GitHubActionClient.get_workflow_name(cmd=cmd, repo=repo, branch_name=branch, container_app_name=name, token=token) + if workflow_name is not None: + if trigger_existing_workflow: + trigger_workflow(token, repo, workflow_name, branch) + return source_control_info + except: # pylint: disable=bare-except + pass + source_control_info["properties"]["repoUrl"] = repo_url source_control_info["properties"]["branch"] = branch @@ -2834,7 +2845,20 @@ def create_or_update_github_action(cmd, logger.warning("Creating Github action...") r = GitHubActionClient.create_or_update(cmd=cmd, resource_group_name=resource_group_name, name=name, github_action_envelope=source_control_info, headers=headers, no_wait=no_wait) if not no_wait: - await_github_action(token, repo, r["properties"]["githubActionConfiguration"]["workflowName"]) + WORKFLOW_POLL_RETRY = 3 + WORKFLOW_POLL_SLEEP = 10 + + # Poll for the workflow file just created (may take up to 30s) + for _ in range(0, WORKFLOW_POLL_RETRY): + time.sleep(WORKFLOW_POLL_SLEEP) + workflow_name = GitHubActionClient.get_workflow_name(cmd=cmd, repo=repo, branch_name=branch, container_app_name=name, token=token) + if workflow_name is not None: + await_github_action(token, repo, workflow_name) + return r + + raise ValidationError( + "Failed to find workflow file for Container App '{}' in .github/workflow folder for repo '{}'. ".format(name, repo) + + "If this file was removed, please use the 'az containerapp github-action delete' command to disconnect the removed workflow file connection.") return r except Exception as e: handle_raw_exception(e) diff --git a/src/containerapp/setup.py b/src/containerapp/setup.py index 32ae102c998..665335575e2 100644 --- a/src/containerapp/setup.py +++ b/src/containerapp/setup.py @@ -28,7 +28,7 @@ # TODO: Confirm this is the right version number you want and it matches your # HISTORY.rst entry. -VERSION = '0.3.33' +VERSION = '0.3.34' # The full list of classifiers is available at # https://pypi.python.org/pypi?%3Aaction=list_classifiers