Skip to content
Closed
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
ee81cde
add integration test workflow file
ezhang6811 Nov 5, 2025
e964a52
add test path for init script
ezhang6811 Nov 5, 2025
f67fd9e
add raw file to output for tool validation
ezhang6811 Nov 5, 2025
2cad45f
fix test trigger
ezhang6811 Nov 5, 2025
0398baa
fix testMode undefined
ezhang6811 Nov 5, 2025
77b7770
fix raw output fetch
ezhang6811 Nov 5, 2025
0558a18
add debugging for fetching raw output
ezhang6811 Nov 5, 2025
12a3d06
fix file path for raw output
ezhang6811 Nov 5, 2025
beeb1d6
update tool call validation
ezhang6811 Nov 5, 2025
9063330
fix matching pattern for tool call
ezhang6811 Nov 5, 2025
3000f10
add soak test and main build triggers
ezhang6811 Nov 7, 2025
823a2c1
add step to publish metric
ezhang6811 Nov 7, 2025
daa011e
update job permission
ezhang6811 Nov 7, 2025
8e804f6
use env var for region
ezhang6811 Nov 7, 2025
249742f
Merge branch 'main' into integ-test
ezhang6811 Nov 10, 2025
cb0ac99
Merge branch 'main' into integ-test
ezhang6811 Nov 14, 2025
342531a
split into separate workflows
ezhang6811 Nov 14, 2025
a955d48
use configure-aws-credentials action
ezhang6811 Nov 14, 2025
fd53e59
fix
ezhang6811 Nov 14, 2025
df87747
add custom prompt and extract verification logic
ezhang6811 Nov 14, 2025
18983a2
checkout before verify step
ezhang6811 Nov 14, 2025
44067af
update log validation
ezhang6811 Nov 14, 2025
1e6003d
restrict test mode permissions
ezhang6811 Nov 14, 2025
5dd3a42
update permission
ezhang6811 Nov 14, 2025
7b4b723
only provide raw output in test mode
ezhang6811 Nov 15, 2025
053f7e5
remove tracing mode
ezhang6811 Nov 15, 2025
79ff874
fix indentation
ezhang6811 Nov 17, 2025
4c1c488
remove integ-test trigger
ezhang6811 Nov 19, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions .github/actions/verify-mcp-usage/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
name: "Verify MCP Usage"
description: "Verifies that both CloudWatch and Application Signals MCP tools were called"

inputs:
artifact_name:
description: "Name of the artifact containing test outputs"
required: true
output_path:
description: "Path to download test outputs"
required: false
default: "./test-outputs"

runs:
using: "composite"
steps:
- name: Download test outputs
uses: actions/download-artifact@v4
with:
name: ${{ inputs.artifact_name }}
path: ${{ inputs.output_path }}

- name: Verify MCP tool usage
shell: bash
run: |
echo "Checking for MCP tool usage..."

RAW_OUTPUT_FILE="${{ inputs.output_path }}/awsapm-raw-output.txt"

if [[ ! -f "$RAW_OUTPUT_FILE" ]]; then
echo "ERROR: Raw output file not found"
exit 1
fi

# Check for both MCP tool calls
if grep -qE "Using tool: list_monitored_services.*from mcp server.*applicationsignals" "$RAW_OUTPUT_FILE"; then
echo "PASSED - list_monitored_services tool was called from AWS Application Signals MCP"
else
echo "FAILED: list_monitored_services tool call not found"
echo "Raw output content:"
cat "$RAW_OUTPUT_FILE"
exit 1
fi

if grep -qE "Using tool: get_active_alarms.*from mcp server.*awslabs\.cloudwatch-mcp-server" "$RAW_OUTPUT_FILE"; then
echo "PASSED - get_active_alarms tool was called from CloudWatch MCP"
else
echo "FAILED: get_active_alarms tool call not found"
echo "Raw output content:"
cat "$RAW_OUTPUT_FILE"
exit 1
fi
57 changes: 57 additions & 0 deletions .github/workflows/integ-test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
name: Integration Test
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this test will be triggered in 2 scenarios?

  1. PR push
  2. Periodical check like soaking test? should we emit metrics on result and alarm for any issues

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added two triggers. One is a main build (PR merged to main), the other is a soak test (every 6 hours but can be reduced). Currently both point to the source code; once we release the initial version of the action I will update the soak test trigger uses: aws-actions/apm-action@v1.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added job to publish success metric.


on:
push:
branches:
- integ-test
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

temporary manual trigger, remove before merging

- main
workflow_dispatch:

env:
AWS_DEFAULT_REGION: us-east-1

jobs:
awsapm-integ-test:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
issues: write
id-token: write
steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWSAPM_ROLE_ARN }}
aws-region: ${{ env.AWS_DEFAULT_REGION }}

- name: Run Application Observability for AWS Investigation
uses: ./
with:
test_mode: "true"
custom_prompt: "Use the list_monitored_services tool to show me all services currently monitored by AWS Application Signals. Also use the get_active_alarms tool to show me all active CloudWatch alarms."

- name: Upload test outputs
if: always()
uses: actions/upload-artifact@v4
with:
name: integration-test-outputs
path: |
${{ runner.temp }}/awsapm-output/
retention-days: 7

verify-mcp-usage:
needs: awsapm-integ-test
runs-on: ubuntu-latest
if: always()
steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Verify MCP tool usage
uses: ./.github/actions/verify-mcp-usage
with:
artifact_name: integration-test-outputs
81 changes: 81 additions & 0 deletions .github/workflows/soak-test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
name: Soak Test

on:
schedule:
- cron: '0 * * * *' # every hour

env:
AWS_DEFAULT_REGION: us-east-1

jobs:
awsapm-soak-test:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
issues: write
id-token: write
steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWSAPM_ROLE_ARN }}
aws-region: ${{ env.AWS_DEFAULT_REGION }}

- name: Run Application Observability for AWS Investigation - Soak Test
# TODO: Change to use published action once v1 is released
# uses: aws-actions/application-observability-for-aws@v1
uses: ./
with:
test_mode: "true"
custom_prompt: "Use the list_monitored_services tool to show me all services currently monitored by AWS Application Signals. Also use the get_active_alarms tool to show me all active CloudWatch alarms."

- name: Upload test outputs
if: always()
uses: actions/upload-artifact@v4
with:
name: soak-test-outputs
path: |
${{ runner.temp }}/awsapm-output/
retention-days: 7

verify-mcp-usage:
needs: awsapm-soak-test
runs-on: ubuntu-latest
if: always()
steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Verify MCP tool usage
uses: ./.github/actions/verify-mcp-usage
with:
artifact_name: soak-test-outputs

publish-metric:
needs: [awsapm-soak-test, verify-mcp-usage]
runs-on: ubuntu-latest
if: always()
permissions:
id-token: write
steps:
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@a03048d87541d1d9fcf2ecf528a4a65ba9bd7838 #5.0.0
with:
role-to-assume: ${{ secrets.MONITORING_ROLE_ARN }}
aws-region: ${{ env.AWS_DEFAULT_REGION }}

- name: Publish soak test status
run: |
if [[ "${{ needs.awsapm-soak-test.result }}" == "success" && "${{ needs.verify-mcp-usage.result }}" == "success" ]]; then
value="1.0"
else
value="0.0"
fi

aws cloudwatch put-metric-data \
--namespace 'ADOT/GithubActions' \
--metric-data MetricName=Success,Value=$value,Dimensions="[{Name=repository,Value=${{ github.repository }}},{Name=branch,Value=${{ github.ref_name }}},{Name=workflow,Value=soak_test}]"
15 changes: 15 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ inputs:
description: "Enable CloudWatch MCP server for metrics, alarms, and log insights"
required: false
default: "true"
test_mode:
description: "Enable integration test mode (internal use only)"
required: false
default: "false"

outputs:
execution_file:
Expand All @@ -47,6 +51,15 @@ outputs:
runs:
using: "composite"
steps:
- name: Validate test mode usage
if: inputs.test_mode == 'true'
shell: bash
run: |
if [[ "${{ github.repository }}" != "aws-actions/application-observability-for-aws" || "${{ github.ref_name }}" != "main" ]]; then
echo "::error::test_mode can only be used from the aws-actions/application-observability-for-aws repository main branch"
exit 1
fi

- name: Install Node.js
uses: actions/setup-node@v4
with:
Expand All @@ -72,6 +85,8 @@ runs:
ALLOWED_NON_WRITE_USERS: ${{ inputs.allowed_non_write_users }}
GITHUB_RUN_ID: ${{ github.run_id }}
DEFAULT_WORKFLOW_TOKEN: ${{ github.token }}
TRACING_MODE: ${{ inputs.tracing_mode }}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why tracing mode got enabled?

TEST_MODE: ${{ inputs.test_mode }}

- name: Install CLI Tools
if: steps.init.outputs.contains_trigger == 'true'
Expand Down
12 changes: 12 additions & 0 deletions src/execute.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,16 @@ async function run() {

// Run Amazon Q Developer CLI investigation
let investigationResult = '';
let rawOutput = '';

try {
core.info('Running Amazon Q Developer CLI investigation...');
const executor = new AmazonQCLIExecutor(null);

executor.onRawOutput = (output) => {
rawOutput = output;
};

investigationResult = await executor.execute(promptContent);
core.info('Amazon Q Developer CLI investigation completed');
} catch (error) {
Expand All @@ -63,6 +69,12 @@ Please check the workflow logs for more details and ensure proper authentication
const responseFile = path.join(outputDir, `awsapm-response-${runId}.txt`);
fs.writeFileSync(responseFile, cleanedResult);

// Save raw output for integration test validation
if (rawOutput) {
const rawOutputFile = path.join(outputDir, 'awsapm-raw-output.txt');
fs.writeFileSync(rawOutputFile, rawOutput);
}

// Set outputs
core.setOutput('execution_file', responseFile);
core.setOutput('conclusion', 'success');
Expand Down
4 changes: 4 additions & 0 deletions src/executors/base-cli-executor.js
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,10 @@ class BaseCLIExecutor {
const output = stdoutData || stderrData;
core.debug(`[SUMMARY] Using ${stdoutData ? 'stdout' : 'stderr'} as output source`);

if (this.onRawOutput) {
this.onRawOutput(output);
}

resolve({ output, exitCode: code || 0 });
});

Expand Down
44 changes: 33 additions & 11 deletions src/init.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ async function run() {
const targetBranch = process.env.TARGET_BRANCH || '';
const allowedNonWriteUsers = process.env.ALLOWED_NON_WRITE_USERS || '';
const customPrompt = process.env.CUSTOM_PROMPT || '';
const tracingMode = process.env.TRACING_MODE || 'false';
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the same

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed both instances

const testMode = process.env.TEST_MODE || 'false';

// Function to check for bot name trigger phrase
// Note: Phrases like "@awsapm-user" will be also considered valid.
Expand Down Expand Up @@ -74,6 +76,11 @@ async function run() {
// We still want to search for existing result comments when editing
commentId = null; // Explicitly set to null for clarity
}
} else if (testMode === 'true') {
// In test mode, always trigger and use custom prompt
containsTrigger = true;
triggerText = customPrompt;
triggerUsername = 'integration-test';
}

// Set output for action.yml to check
Expand Down Expand Up @@ -233,25 +240,35 @@ async function run() {
// Remove bot name from the user's request
const cleanedUserRequest = triggerText.replace(new RegExp(botName, 'gi'), '').trim();

// Use the dynamic prompt generation with PR context
const { createGeneralPrompt } = require('./prompt-builder');
if (testMode === 'true') {
// for integration test, use custom prompt directly
try {
fs.writeFileSync(promptFile, customPrompt);
} catch (error) {
core.error(`Failed to write custom prompt to file: ${error.message}`);
process.exit(1);
}
} else {
// Use the dynamic prompt generation with PR context
const { createGeneralPrompt } = require('./prompt-builder');

try {
const finalPrompt = await createGeneralPrompt(context, repoInfo, cleanedUserRequest, githubToken, awsapmBranch);
fs.writeFileSync(promptFile, finalPrompt);
} catch (promptError) {
core.error(`Failed to generate dynamic prompt: ${promptError.message}`);

// Fallback to basic prompt if dynamic generation fails
let fallbackPrompt = '';
if (customPrompt) {
fallbackPrompt = customPrompt + '\n\n';
}
fallbackPrompt += `Please analyze this ${isPR ? 'pull request' : 'issue'} using AI Agent for insights.\n\n`;
fallbackPrompt += `Original request: ${cleanedUserRequest}\n\n`;
fallbackPrompt += `Context: This is a ${context.eventName} event in ${context.repo.owner}/${context.repo.repo}`;
// Fallback to basic prompt if dynamic generation fails
let fallbackPrompt = '';
Comment thread
mxiamxia marked this conversation as resolved.
if (customPrompt) {
fallbackPrompt = customPrompt + '\n\n';
}
fallbackPrompt += `Please analyze this ${isPR ? 'pull request' : 'issue'} using AI Agent for insights.\n\n`;
fallbackPrompt += `Original request: ${cleanedUserRequest}\n\n`;
fallbackPrompt += `Context: This is a ${context.eventName} event in ${context.repo.owner}/${context.repo.repo}`;

fs.writeFileSync(promptFile, fallbackPrompt);
fs.writeFileSync(promptFile, fallbackPrompt);
}
}

// Set outputs
Expand All @@ -275,6 +292,11 @@ async function run() {
* Check if user has write or admin permissions to the repository
*/
async function checkUserPermissions(octokit, context, issueNumber, allowedNonWriteUsers) {
const testMode = process.env.TEST_MODE || 'false';
if (testMode === 'true') {
return true;
}

const actor = context.actor;
core.debug(`Checking permissions for actor: ${actor}`);

Expand Down
Loading