Skip to content

Commit

Permalink
Optimize environment parsing and improve markdown handling in CLI and…
Browse files Browse the repository at this point in the history
… sample scripts
  • Loading branch information
pelikhan committed Sep 26, 2024
1 parent 67a8242 commit f48627d
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 74 deletions.
43 changes: 24 additions & 19 deletions packages/cli/src/run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
githubCreatePullRequestReviews,
githubUpdatePullRequestDescription,
githubParseEnv,
GithubConnectionInfo,
} from "../../core/src/github"
import {
HTTPS_REGEX,
Expand Down Expand Up @@ -61,12 +62,14 @@ import { PromptScriptRunOptions } from "../../core/src/server/messages"
import { writeFileEdits } from "../../core/src/edits"
import {
azureDevOpsCreateIssueComment,
AzureDevOpsEnv,
azureDevOpsParseEnv,
azureDevOpsUpdatePullRequestDescription,
} from "../../core/src/azuredevops"
import { resolveTokenEncoder } from "../../core/src/encoders"
import { writeFile } from "fs/promises"
import { writeFileSync } from "node:fs"
import { prettifyMarkdown } from "../../core/src/markdown"

async function setupTraceWriting(trace: MarkdownTrace, filename: string) {
logVerbose(`trace: ${filename}`)
Expand Down Expand Up @@ -424,37 +427,39 @@ export async function runScript(
}
}

let ghInfo: GithubConnectionInfo = undefined

Check failure on line 430 in packages/cli/src/run.ts

View workflow job for this annotation

GitHub Actions / build

Initializing `ghInfo` and `adoInfo` to `undefined` is unnecessary as variables in TypeScript are `undefined` by default. You can simply declare them without initialization. 😊
let adoInfo: AzureDevOpsEnv = undefined
if (pullRequestReviews && result.annotations?.length) {
// github action or repo
const info = await githubParseEnv(process.env)
if (info.repository && info.issue && info.commitSha) {
ghInfo = ghInfo ?? (await githubParseEnv(process.env))
if (ghInfo.repository && ghInfo.issue && ghInfo.commitSha) {
await githubCreatePullRequestReviews(
script,
info,
ghInfo,
result.annotations
)
}

Check failure on line 441 in packages/cli/src/run.ts

View workflow job for this annotation

GitHub Actions / build

The nullish coalescing operator `??` is unnecessary when assigning `ghInfo` because `ghInfo` is already checked for `undefined` in the if condition. You can directly use `ghInfo = await githubParseEnv(process.env)`.
}

if (pullRequestComment && result.text) {
// github action or repo
const info = await githubParseEnv(process.env)
if (info.repository && info.issue) {
ghInfo = ghInfo ?? (await githubParseEnv(process.env))
if (ghInfo.repository && ghInfo.issue) {
await githubCreateIssueComment(
script,
info,
result.text,
ghInfo,
prettifyMarkdown(result.text),
typeof pullRequestComment === "string"
? pullRequestComment
: script.id
)
} else {
const adoinfo = await azureDevOpsParseEnv(process.env)
if (adoinfo.collectionUri) {
adoInfo = adoInfo ?? (await azureDevOpsParseEnv(process.env))
if (adoInfo.collectionUri) {
await azureDevOpsCreateIssueComment(
script,
adoinfo,
result.text,
adoInfo,
prettifyMarkdown(result.text),
typeof pullRequestComment === "string"
? pullRequestComment
: script.id
Expand All @@ -468,24 +473,24 @@ export async function runScript(

if (pullRequestDescription && result.text) {
// github action or repo
const ghinfo = await githubParseEnv(process.env)
if (ghinfo.repository && ghinfo.issue) {
ghInfo = ghInfo ?? (await githubParseEnv(process.env))
if (ghInfo.repository && ghInfo.issue) {
await githubUpdatePullRequestDescription(
script,
ghinfo,
result.text,
ghInfo,
prettifyMarkdown(result.text),
typeof pullRequestDescription === "string"
? pullRequestDescription
: script.id
)
} else {
// azure devops pipeline
const adoinfo = await azureDevOpsParseEnv(process.env)
if (adoinfo.collectionUri) {
adoInfo = adoInfo ?? (await azureDevOpsParseEnv(process.env))
if (adoInfo.collectionUri) {
await azureDevOpsUpdatePullRequestDescription(
script,
adoinfo,
result.text,
adoInfo,
prettifyMarkdown(result.text),
typeof pullRequestDescription === "string"
? pullRequestDescription
: script.id
Expand Down
89 changes: 34 additions & 55 deletions packages/sample/genaisrc/gai.genai.mts
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,29 @@
import { Octokit } from "octokit"
import { createPatch, createTwoFilesPatch, diffArrays, formatPatch } from "diff"

const workflow = env.vars.workflow || "build.yml"
const lsid: undefined = env.vars.success_run_id
const branch =
env.vars.branch ||
(await host.exec("git branch --show-current")).stdout.trim()

const octokit = new Octokit({
auth: process.env.GITHUB_TOKEN,
})
const { owner, repo } = await getRepoInfo()

script({
system: ["system", "system.files", "system.annotations"],
system: ["system", "system.files"],
cache: "gh-investigator",
})

const workflow = env.vars.workflow || "build.yml"
const lsid = 11025993709
const runs = await listRuns(workflow, branch)

const runs = await listRuns(workflow)

// first first failure, last success
// first last success
const lsi = lsid
? runs.findIndex(({ id }) => id === lsid)
: runs.findIndex(({ conclusion }) => conclusion === "success")
const ls = runs[lsi]

console.log(
`> last success: ${ls.id}, ${ls.created_at}, ${ls.head_sha}, ${ls.html_url}`
)
Expand Down Expand Up @@ -50,47 +52,42 @@ console.log(
`> first failure log: ${(fflog.length / 1000) | 0}kb ${ffjob.logUrl}`
)

const pr = await getPullRequestForRun(ff.id)
if (pr) console.log(`> PR: ${pr.number}, ${pr.title}, ${pr.html_url}`)

const logDiff = diffJobLogs(lslog, fflog)
console.log(`> log diff: ${(logDiff.length / 1000) | 0}kb`)

const res = await runPrompt(
(_) => {
// include difss
_.def("GIT_DIFF", gitDiff, {
language: "diff",
maxTokens: 10000,
lineNumbers: false,
})
_.def("LOG_DIFF", logDiff, {
language: "diff",
maxTokens: 20000,
lineNumbers: false,
})
_.$`Your are an expert software engineer and you are able to analyze the logs and find the root cause of the failure.
// include difss
def("GIT_DIFF", gitDiff, {
language: "diff",
maxTokens: 10000,
lineNumbers: true,
})
def("LOG_DIFF", logDiff, {
language: "diff",
maxTokens: 20000,
lineNumbers: false,
})
$`Your are an expert software engineer and you are able to analyze the logs and find the root cause of the failure.
- GIT_DIFF contains a diff of 2 run commits
- LOG_DIFF contains a diff of 2 runs in GitHub Action
- The first run is the last successful run and the second run is the first failed run
## Task 1
Add links to run logs.
Analyze the diff in LOG_DIFF and provide a summary of the root cause of the failure.
Analyze the diff in LOG_DIFF and provide a summary of the root cause of the failure.
If you cannot find the root cause, stop.
## Task 2
Generate updates for the source files that can fix the issue.
- use a unified diff format compatible with diff
Generate a diff with suggested fixes. Use a diff format.`

## Task 3
writeText(
`## Investigator report
- [run first failure](${ff.html_url})
- [run last success](${ls.html_url})
- [commit diff](https://github.com/${owner}/${repo}/compare/${ls.head_sha}...${ff.head_sha})
Generate annotations in the source code to help identify the root cause of the failure.`
},
{ cache: "gai" }
`,
{ assistant: true }
)

/*-----------------------------------------
Expand All @@ -116,14 +113,15 @@ async function getRepoInfo() {
return { owner, repo }
}

async function listRuns(workflow_id: string) {
async function listRuns(workflow_id: string, branch: string) {
// Get the workflow runs for the specified workflow file, filtering for failures
const {
data: { workflow_runs },
} = await octokit.rest.actions.listWorkflowRuns({
owner,
repo,
workflow_id,
branch,
per_page: 100,
})
const runs = workflow_runs.filter(
Expand All @@ -132,7 +130,7 @@ async function listRuns(workflow_id: string) {
return runs
}

async function downloadR unLog(run_id: number) {
async function downloadRunLog(run_id: number) {
const res = []
// Get the jobs for the specified workflow run
const {
Expand All @@ -155,25 +153,6 @@ async function downloadR unLog(run_id: number) {
return res
}

async function getPullRequestForRun(run_id: number) {
const { data: workflowRun } = await octokit.rest.actions.getWorkflowRun({
owner,
repo,
run_id,
})
if (workflowRun.event === "pull_request") {
const url = workflowRun.pull_requests?.[0]?.url
if (url) {
// Fetch the pull request details
const { data: pullRequest } = await octokit.request(
workflowRun.pull_requests[0].url
)
return pullRequest
}
}
return null
}

function diffJobLogs(firstLog: string, otherLog: string) {
let firsts = parseJobLog(firstLog)
let others = parseJobLog(otherLog)
Expand Down

0 comments on commit f48627d

Please sign in to comment.