diff --git a/samcli/commands/build/build_context.py b/samcli/commands/build/build_context.py index dc5b6726f3..7f1f5bff4b 100644 --- a/samcli/commands/build/build_context.py +++ b/samcli/commands/build/build_context.py @@ -270,8 +270,6 @@ def run(self): for f in self.get_resources_to_build().functions: EventTracker.track_event("BuildFunctionRuntime", f.runtime) - if f.metadata and f.metadata.get("BuildMethod", "") == "esbuild": - EventTracker.track_event("UsedFeature", "ESBuild") click.secho("\nBuild Succeeded", fg="green") diff --git a/samcli/lib/build/workflow_config.py b/samcli/lib/build/workflow_config.py index 3a006ca2e4..d6b1f55926 100644 --- a/samcli/lib/build/workflow_config.py +++ b/samcli/lib/build/workflow_config.py @@ -4,95 +4,24 @@ import os import logging -from collections import namedtuple from typing import Dict, List, Optional, Tuple, Union, cast -LOG = logging.getLogger(__name__) - -CONFIG = namedtuple( - "Capability", - ["language", "dependency_manager", "application_framework", "manifest_name", "executable_search_paths"], -) - -PYTHON_PIP_CONFIG = CONFIG( - language="python", - dependency_manager="pip", - application_framework=None, - manifest_name="requirements.txt", - executable_search_paths=None, -) - -NODEJS_NPM_CONFIG = CONFIG( - language="nodejs", - dependency_manager="npm", - application_framework=None, - manifest_name="package.json", - executable_search_paths=None, -) - -RUBY_BUNDLER_CONFIG = CONFIG( - language="ruby", - dependency_manager="bundler", - application_framework=None, - manifest_name="Gemfile", - executable_search_paths=None, -) - -JAVA_GRADLE_CONFIG = CONFIG( - language="java", - dependency_manager="gradle", - application_framework=None, - manifest_name="build.gradle", - executable_search_paths=None, -) - -JAVA_KOTLIN_GRADLE_CONFIG = CONFIG( - language="java", - dependency_manager="gradle", - application_framework=None, - manifest_name="build.gradle.kts", - executable_search_paths=None, +from samcli.lib.build.workflows import ( + CONFIG, + PYTHON_PIP_CONFIG, + NODEJS_NPM_CONFIG, + RUBY_BUNDLER_CONFIG, + JAVA_GRADLE_CONFIG, + JAVA_KOTLIN_GRADLE_CONFIG, + JAVA_MAVEN_CONFIG, + DOTNET_CLIPACKAGE_CONFIG, + GO_MOD_CONFIG, + PROVIDED_MAKE_CONFIG, + NODEJS_NPM_ESBUILD_CONFIG, ) +from samcli.lib.telemetry.event import EventTracker -JAVA_MAVEN_CONFIG = CONFIG( - language="java", - dependency_manager="maven", - application_framework=None, - manifest_name="pom.xml", - executable_search_paths=None, -) - -DOTNET_CLIPACKAGE_CONFIG = CONFIG( - language="dotnet", - dependency_manager="cli-package", - application_framework=None, - manifest_name=".csproj", - executable_search_paths=None, -) - -GO_MOD_CONFIG = CONFIG( - language="go", - dependency_manager="modules", - application_framework=None, - manifest_name="go.mod", - executable_search_paths=None, -) - -PROVIDED_MAKE_CONFIG = CONFIG( - language="provided", - dependency_manager=None, - application_framework=None, - manifest_name="Makefile", - executable_search_paths=None, -) - -NODEJS_NPM_ESBUILD_CONFIG = CONFIG( - language="nodejs", - dependency_manager="npm-esbuild", - application_framework=None, - manifest_name="package.json", - executable_search_paths=None, -) +LOG = logging.getLogger(__name__) class UnsupportedRuntimeException(Exception): @@ -278,6 +207,9 @@ def get_workflow_config( # Identify workflow configuration from the workflow selector. config = cast(WorkFlowSelector, selector).get_config(code_dir, project_dir) + + EventTracker.track_event("BuildWorkflowUsed", f"{config.language}-{config.dependency_manager}") + return config except ValueError as ex: raise UnsupportedRuntimeException( diff --git a/samcli/lib/build/workflows.py b/samcli/lib/build/workflows.py new file mode 100644 index 0000000000..a413f696c1 --- /dev/null +++ b/samcli/lib/build/workflows.py @@ -0,0 +1,102 @@ +"""Module for storing information about existing workflows.""" + +from collections import namedtuple +from typing import List + +CONFIG = namedtuple( + "Capability", + ["language", "dependency_manager", "application_framework", "manifest_name", "executable_search_paths"], +) + +PYTHON_PIP_CONFIG = CONFIG( + language="python", + dependency_manager="pip", + application_framework=None, + manifest_name="requirements.txt", + executable_search_paths=None, +) + +NODEJS_NPM_CONFIG = CONFIG( + language="nodejs", + dependency_manager="npm", + application_framework=None, + manifest_name="package.json", + executable_search_paths=None, +) + +RUBY_BUNDLER_CONFIG = CONFIG( + language="ruby", + dependency_manager="bundler", + application_framework=None, + manifest_name="Gemfile", + executable_search_paths=None, +) + +JAVA_GRADLE_CONFIG = CONFIG( + language="java", + dependency_manager="gradle", + application_framework=None, + manifest_name="build.gradle", + executable_search_paths=None, +) + +JAVA_KOTLIN_GRADLE_CONFIG = CONFIG( + language="java", + dependency_manager="gradle", + application_framework=None, + manifest_name="build.gradle.kts", + executable_search_paths=None, +) + +JAVA_MAVEN_CONFIG = CONFIG( + language="java", + dependency_manager="maven", + application_framework=None, + manifest_name="pom.xml", + executable_search_paths=None, +) + +DOTNET_CLIPACKAGE_CONFIG = CONFIG( + language="dotnet", + dependency_manager="cli-package", + application_framework=None, + manifest_name=".csproj", + executable_search_paths=None, +) + +GO_MOD_CONFIG = CONFIG( + language="go", + dependency_manager="modules", + application_framework=None, + manifest_name="go.mod", + executable_search_paths=None, +) + +PROVIDED_MAKE_CONFIG = CONFIG( + language="provided", + dependency_manager=None, + application_framework=None, + manifest_name="Makefile", + executable_search_paths=None, +) + +NODEJS_NPM_ESBUILD_CONFIG = CONFIG( + language="nodejs", + dependency_manager="npm-esbuild", + application_framework=None, + manifest_name="package.json", + executable_search_paths=None, +) + +ALL_CONFIGS: List[CONFIG] = [ + PYTHON_PIP_CONFIG, + NODEJS_NPM_CONFIG, + RUBY_BUNDLER_CONFIG, + JAVA_GRADLE_CONFIG, + JAVA_KOTLIN_GRADLE_CONFIG, + JAVA_MAVEN_CONFIG, + DOTNET_CLIPACKAGE_CONFIG, + GO_MOD_CONFIG, + PROVIDED_MAKE_CONFIG, + NODEJS_NPM_ESBUILD_CONFIG, +] diff --git a/samcli/lib/telemetry/event.py b/samcli/lib/telemetry/event.py index 906b2d56a3..2ddaf39b15 100644 --- a/samcli/lib/telemetry/event.py +++ b/samcli/lib/telemetry/event.py @@ -8,6 +8,7 @@ import threading from typing import List +from samcli.lib.build.workflows import ALL_CONFIGS from samcli.lib.telemetry.telemetry import Telemetry from samcli.local.common.runtime_template import INIT_RUNTIMES @@ -19,11 +20,11 @@ class EventName(Enum): """Enum for the names of available events to track.""" USED_FEATURE = "UsedFeature" - DEPLOY = "Deploy" BUILD_FUNCTION_RUNTIME = "BuildFunctionRuntime" SYNC_USED = "SyncUsed" SYNC_FLOW_START = "SyncFlowStart" SYNC_FLOW_END = "SyncFlowEnd" + BUILD_WORKFLOW_USED = "BuildWorkflowUsed" class EventType: @@ -43,18 +44,12 @@ class EventType: "StepFunctionsSyncFlow", "ZipFunctionSyncFlow", ] + _WORKFLOWS = [f"{config.language}-{config.dependency_manager}" for config in ALL_CONFIGS] _events = { EventName.USED_FEATURE: [ - "ESBuild", "Accelerate", "CDK", ], - EventName.DEPLOY: [ - "CreateChangeSetStart", - "CreateChangeSetInProgress", - "CreateChangeSetFailed", - "CreateChangeSetSuccess", - ], EventName.BUILD_FUNCTION_RUNTIME: INIT_RUNTIMES, EventName.SYNC_USED: [ "Start", @@ -62,6 +57,7 @@ class EventType: ], EventName.SYNC_FLOW_START: _SYNC_FLOWS, EventName.SYNC_FLOW_END: _SYNC_FLOWS, + EventName.BUILD_WORKFLOW_USED: _WORKFLOWS, } @staticmethod diff --git a/tests/unit/lib/build_module/test_workflow_config.py b/tests/unit/lib/build_module/test_workflow_config.py index 52549a3718..e35d6c05fe 100644 --- a/tests/unit/lib/build_module/test_workflow_config.py +++ b/tests/unit/lib/build_module/test_workflow_config.py @@ -7,12 +7,14 @@ UnsupportedRuntimeException, UnsupportedBuilderException, ) +from samcli.lib.telemetry.event import Event, EventTracker class Test_get_workflow_config(TestCase): def setUp(self): self.code_dir = "" self.project_dir = "" + EventTracker.clear_trackers() @parameterized.expand([("python3.6",), ("python3.7",), ("python3.8",)]) def test_must_work_for_python(self, runtime): @@ -23,6 +25,8 @@ def test_must_work_for_python(self, runtime): self.assertEqual(result.application_framework, None) self.assertEqual(result.manifest_name, "requirements.txt") self.assertIsNone(result.executable_search_paths) + self.assertEqual(len(EventTracker.get_tracked_events()), 1) + self.assertIn(Event("BuildWorkflowUsed", "python-pip"), EventTracker.get_tracked_events()) @parameterized.expand([("nodejs12.x",), ("nodejs14.x",), ("nodejs16.x",)]) def test_must_work_for_nodejs(self, runtime): @@ -33,6 +37,8 @@ def test_must_work_for_nodejs(self, runtime): self.assertEqual(result.application_framework, None) self.assertEqual(result.manifest_name, "package.json") self.assertIsNone(result.executable_search_paths) + self.assertEqual(len(EventTracker.get_tracked_events()), 1) + self.assertIn(Event("BuildWorkflowUsed", "nodejs-npm"), EventTracker.get_tracked_events()) @parameterized.expand([("provided",)]) def test_must_work_for_provided(self, runtime): @@ -42,6 +48,8 @@ def test_must_work_for_provided(self, runtime): self.assertEqual(result.application_framework, None) self.assertEqual(result.manifest_name, "Makefile") self.assertIsNone(result.executable_search_paths) + self.assertEqual(len(EventTracker.get_tracked_events()), 1) + self.assertIn(Event("BuildWorkflowUsed", "provided-None"), EventTracker.get_tracked_events()) @parameterized.expand([("provided",)]) def test_must_work_for_provided_with_no_specified_workflow(self, runtime): @@ -52,6 +60,8 @@ def test_must_work_for_provided_with_no_specified_workflow(self, runtime): self.assertEqual(result.application_framework, None) self.assertEqual(result.manifest_name, "Makefile") self.assertIsNone(result.executable_search_paths) + self.assertEqual(len(EventTracker.get_tracked_events()), 1) + self.assertIn(Event("BuildWorkflowUsed", "provided-None"), EventTracker.get_tracked_events()) @parameterized.expand([("provided",)]) def test_raise_exception_for_bad_specified_workflow(self, runtime): @@ -66,6 +76,8 @@ def test_must_work_for_ruby(self, runtime): self.assertEqual(result.application_framework, None) self.assertEqual(result.manifest_name, "Gemfile") self.assertIsNone(result.executable_search_paths) + self.assertEqual(len(EventTracker.get_tracked_events()), 1) + self.assertIn(Event("BuildWorkflowUsed", "ruby-bundler"), EventTracker.get_tracked_events()) @parameterized.expand( [("java8", "build.gradle", "gradle"), ("java8", "build.gradle.kts", "gradle"), ("java8", "pom.xml", "maven")] @@ -80,11 +92,14 @@ def test_must_work_for_java(self, runtime, build_file, dep_manager, os_mock): self.assertEqual(result.dependency_manager, dep_manager) self.assertEqual(result.application_framework, None) self.assertEqual(result.manifest_name, build_file) + self.assertEqual(len(EventTracker.get_tracked_events()), 1) if dep_manager == "gradle": self.assertEqual(result.executable_search_paths, [self.code_dir, self.project_dir]) + self.assertIn(Event("BuildWorkflowUsed", "java-gradle"), EventTracker.get_tracked_events()) else: self.assertIsNone(result.executable_search_paths) + self.assertIn(Event("BuildWorkflowUsed", "java-maven"), EventTracker.get_tracked_events()) def test_must_get_workflow_for_esbuild(self): runtime = "nodejs12.x" @@ -94,6 +109,8 @@ def test_must_get_workflow_for_esbuild(self): self.assertEqual(result.application_framework, None) self.assertEqual(result.manifest_name, "package.json") self.assertIsNone(result.executable_search_paths) + self.assertEqual(len(EventTracker.get_tracked_events()), 1) + self.assertIn(Event("BuildWorkflowUsed", "nodejs-npm-esbuild"), EventTracker.get_tracked_events()) @parameterized.expand([("java8", "unknown.manifest")]) @patch("samcli.lib.build.workflow_config.os") diff --git a/tests/unit/lib/telemetry/test_event.py b/tests/unit/lib/telemetry/test_event.py index 24e6e29b1f..68d70b4680 100644 --- a/tests/unit/lib/telemetry/test_event.py +++ b/tests/unit/lib/telemetry/test_event.py @@ -69,6 +69,9 @@ def test_event_to_json(self, name_mock, type_mock, verify_mock): class TestEventTracker(TestCase): + def setUp(self): + EventTracker.clear_trackers() + @patch("samcli.lib.telemetry.event.EventTracker._event_lock") @patch("samcli.lib.telemetry.event.Event") def test_track_event(self, event_mock, lock_mock):