Skip to content
Closed
Show file tree
Hide file tree
Changes from 10 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
77 changes: 77 additions & 0 deletions .github/workflows/integ-test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
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:
schedule:
- cron: '0 */6 * * *'
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


jobs:
awsapm-investigation:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
issues: write
id-token: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 1

- name: Run Application observability for AWS Investigation
id: awsapm
uses: ./
with:
aws_access_key_id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws_secret_access_key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
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.

TODO: let's change this to use IAM role later

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.

will address in next PR.

aws_region: ${{ vars.AWS_REGION || 'us-east-1' }}
bot_name: "@awsapm"
tracing_mode: "true"
test_mode: "true" # Enable integration test mode
custom_prompt: "Use the list_monitored_services tool to show me all services currently monitored by AWS Application Signals. I need to see the complete list of monitored services."

- 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-investigation
runs-on: ubuntu-latest
if: always()
steps:
- name: Download test outputs
uses: actions/download-artifact@v4
with:
name: integration-test-outputs
path: ./test-outputs

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

RAW_OUTPUT_FILE="./test-outputs/awsapm-raw-output.txt"

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

# Check for the specific tool call on one line (handling ANSI color codes)
if grep -qE "Using tool: list_monitored_services.*from mcp server.*awslabs\.cloudwatch-appsignals-mcp" "$RAW_OUTPUT_FILE"; then
echo "PASSED - list_monitored_services tool was called from AWS Application Signals MCP"
else
echo "FAILED: Expected tool call not found"
echo "Looking for: Using tool: list_monitored_services...from mcp server...awslabs.cloudwatch-appsignals-mcp"
echo "Raw output content:"
cat "$RAW_OUTPUT_FILE"
exit 1
fi
5 changes: 5 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ inputs:
description: "Enable tracing to show AI agent reasoning steps and MCP tool calls in workflow summary"
required: false
default: "true"
test_mode:
description: "Enable integration test mode (internal use only)"
required: false
default: "false"

outputs:
execution_file:
Expand Down Expand Up @@ -86,6 +90,7 @@ runs:
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 @@ -34,10 +34,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 @@ -58,6 +64,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, investigationResult);

// 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
55 changes: 38 additions & 17 deletions src/init.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ async function run() {
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 @@ -72,6 +73,11 @@ async function run() {
isEditEvent = payload.action === 'edited';
triggerUsername = issue.user?.login || 'unknown';
}
} 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 @@ -223,25 +229,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');

try {
const finalPrompt = await createGeneralPrompt(context, repoInfo, cleanedUserRequest, githubToken);
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';
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);
}
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}`;
} else {
// Use the dynamic prompt generation with PR context
const { createGeneralPrompt } = require('./prompt-builder');

try {
const finalPrompt = await createGeneralPrompt(context, repoInfo, cleanedUserRequest, githubToken);
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 = '';
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 @@ -266,6 +282,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