action.py (83 lines of code) (raw):

# Copyright (c) Microsoft Corporation. # Licensed under the MIT license. """GitHub Action to retrieve the latest analysis results for an online experiment of an Azure App Configuration feature flag """ import os import sys import uuid from datetime import timedelta from typing import Optional from azure.identity import DefaultAzureCredential from analysis import AnalysisResults, LogAnalyticsWorkspace, latest_analysis, summarize GITHUB_OUTPUT = os.getenv("GITHUB_OUTPUT") GITHUB_STEP_SUMMARY = os.getenv("GITHUB_STEP_SUMMARY") SUBSCRIPTION_ID = os.getenv("SUBSCRIPTION_ID") RESOURCE_GROUP = os.getenv("RESOURCE_GROUP") LOGANALYTICS_WORKSPACE = os.getenv("LOGANALYTICS_WORKSPACE") APPCONFIG_FEATURE_FLAG = os.getenv("APPCONFIG_FEATURE_FLAG") METRIC_CATEGORY_ORDER = [ x.strip() for x in os.getenv("METRIC_CATEGORY_ORDER", "").split(",") if x.strip() ] LOOKBACK_DAYS = int(os.getenv("LOOKBACK_DAYS", "30")) GHA_SUMMARY = os.getenv("GHA_SUMMARY", "true").lower() not in ["false", "0", "no", "n"] # pylint: disable-next=too-many-arguments def main( subscription_id: str, resource_group: str, log_analytics_workspace: str, feature_flag: str, category_order: list[str], lookback_days: int, ) -> tuple[Optional[AnalysisResults], str]: """Retrieve the latest analysis of an experiment and summarize the results. Parameters ---------- subscription_id: str Azure subscription ID. resource_group: str Azure resource group name. log_analytics_workspace: str Azure Log Analytics workspace name. feature_flag: str Azure App Configuration feature flag name. category_order: list[str] Specify the order that metric categories are displayed in the summary. Unspecified categories are appended in alphabetical order, followed by an 'Uncategorized' category. lookback_days: int Number of days to look back for the analysis. Returns ------- tuple[Optional[AnalysisResults], str] Tuple of the analysis results and the summary markdown string. """ workspace = LogAnalyticsWorkspace( subscription_id=subscription_id, resource_group=resource_group, workspace=log_analytics_workspace, ) result = latest_analysis( DefaultAzureCredential(), workspace=workspace, feature_flag=feature_flag, allocation_id=None, timespan=timedelta(days=lookback_days), ) if result is None: summary = f"Analysis unavailable for feature flag '{feature_flag}'" else: summary = summarize(result, category_order=category_order, workspace=workspace) return result, summary def set_github_output(name: str, value: str): """Write multiline string to GitHub Actions output variable""" if GITHUB_OUTPUT: delim = str(uuid.uuid4()) with open(GITHUB_OUTPUT, "a", encoding="utf-8") as f: f.write(f"{name}<<{delim}\n{value}\n{delim}\n") if __name__ == "__main__": if not SUBSCRIPTION_ID: raise ValueError("Missing input: subscription-id") if not RESOURCE_GROUP: raise ValueError("Missing input: resource-group") if not LOGANALYTICS_WORKSPACE: raise ValueError("Missing input: log-analytics-workspace") if not APPCONFIG_FEATURE_FLAG: raise ValueError("Missing input: app-configuration-feature-flag") results, summary_md = main( subscription_id=SUBSCRIPTION_ID, resource_group=RESOURCE_GROUP, log_analytics_workspace=LOGANALYTICS_WORKSPACE, feature_flag=APPCONFIG_FEATURE_FLAG, category_order=METRIC_CATEGORY_ORDER, lookback_days=LOOKBACK_DAYS, ) # if analysis not found, exit gracefully (may be expected) if results is None: title = f"Analysis unavailable: {APPCONFIG_FEATURE_FLAG}" msg = ( f"Can't find analysis results for feature flag '{APPCONFIG_FEATURE_FLAG}'." " Is the first analysis still pending?" ) # highlight within GitHub Action annotations print(f"::warning title={title}::{msg}") sys.exit(0) if results.scorecard.empty: raise ValueError("Found analysis results but unable to download metric data") set_github_output("allocation-id", results.analysis.allocation_id) set_github_output("scorecard-id", results.analysis.scorecard_id) set_github_output("analysis-start-time", results.analysis.start_time.isoformat()) set_github_output("analysis-end-time", results.analysis.end_time.isoformat()) set_github_output("summary-md", summary_md) if GHA_SUMMARY and GITHUB_STEP_SUMMARY: with open(GITHUB_STEP_SUMMARY, "a", encoding="utf-8") as fp: fp.write(summary_md)