From 9f33473a9ff944050657691bc8b63fcfc8079090 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 4 Sep 2026 10:42:08 +0000 Subject: [PATCH 1/2] Initial plan From 8f10bb8cf44132c889ceb30dde17e8a0ca04b96d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 4 Sep 2026 10:53:10 +0000 Subject: [PATCH 2/2] Add duplicate code operational value grader Co-authored-by: mnkiefer <8320933+mnkiefer@users.noreply.github.com> --- ...plicate-code-detector-operational-value.sh | 182 ++++++++++++++++++ .../duplicate-code-detector.lock.yml | 42 +++- .github/workflows/duplicate-code-detector.md | 4 +- 3 files changed, 226 insertions(+), 2 deletions(-) create mode 100755 .github/graders/duplicate-code-detector-operational-value.sh diff --git a/.github/graders/duplicate-code-detector-operational-value.sh b/.github/graders/duplicate-code-detector-operational-value.sh new file mode 100755 index 00000000000..539f9157598 --- /dev/null +++ b/.github/graders/duplicate-code-detector-operational-value.sh @@ -0,0 +1,182 @@ +#!/usr/bin/env bash + +set -euo pipefail + +REPOSITORY=github/gh-aw +WORKFLOW_NAME="Duplicate Code Detector" +MATURATION_SECONDS=1209600 + +tmp_dir=$(mktemp -d "${TMPDIR:-/tmp}/duplicate-code-detector-operational-value.XXXXXX") +trap 'rm -rf "$tmp_dir"' EXIT HUP INT TERM + +definition() { + cat <<'JSON' +{ + "schemaVersion": 4, "grader": "operational-value", + "repository": "github/gh-aw", "workflowName": "Duplicate Code Detector", + "sourcePath": ".github/workflows/duplicate-code-detector.md", + "adoption": {"commit": "c3c00d297e070cd7f766d2c87bb809301912e87b", "adoptedAt": "2025-10-07T06:29:50Z"}, + "operationalValue": "Resolve the duplicate-code reports assigned to a detector run through merged pull requests.", + "evidence": { + "opportunity": "The distinct duplicate-code reports whose generated body binds them to the detector run ID, or for legacy runs its analyzed commit.", + "assignment": "Run ID is primary and analyzed commit SHA is the historical fallback; only '[duplicate-code] Duplicate Code:' issues qualify. The run key repeats for reruns.", + "accepted": "GitHub issue bodies proving assignment and their closing pull requests' merged state and time. Agent output, traces, issue closure alone, and report volume are excluded.", + "repositories": ["github/gh-aw"], + "collection": "With issues:read and pull-requests:read, search issue bodies at the capped cutoff and query each qualifying issue's closing pull requests.", + "maturation": "Fourteen days. Comparable pre-adoption duplicate-report evidence does not exist because this workflow introduced the report format.", + "zeroRule": "A qualifying report without a closing pull request merged by the cutoff contributes 0.", + "missingRule": "No qualifying report, incomplete search results, unavailable issue evidence, or malformed evidence scores null, never 0." + }, + "primaryMetric": {"id": "duplicate-report-remediation-rate", "formula": "mergedReports / qualifyingReports, where a report is attained only when a closing pull request merged no later than the capped cutoff.", "direction": "higher_is_better"}, + "baseline": {"mode": "attainment-only", "value": null, "evidenceCutoff": null, "provenance": []}, + "validationExamples": { + "targetAttained": {"valid":true,"qualifyingReports":2,"mergedReports":2}, + "targetMissed": {"valid":true,"qualifyingReports":2,"mergedReports":0}, + "missing": {"valid":false}, + "malformed": {"valid":"yes","qualifyingReports":"2","mergedReports":2} + } +} +JSON +} + +metric() { + local evidence + evidence=$(cat) + if ! printf '%s\n' "$evidence" | jq -e . >/dev/null 2>&1; then + printf 'null\n' + return + fi + printf '%s\n' "$evidence" | jq ' + if .valid != true + or ([.qualifyingReports,.mergedReports] | all(.[]; type == "number" and floor == .) | not) + or .qualifyingReports <= 0 or .mergedReports < 0 or .mergedReports > .qualifyingReports + then null else .mergedReports / .qualifyingReports end' +} + +normalize_timestamp() { + jq -nr --arg timestamp "$1" ' + ($timestamp | sub("\\.[0-9]+Z$"; "Z")) as $normalized + | if ($normalized | test("^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z$")) + and (try (($normalized | fromdateiso8601 | todateiso8601) == $normalized) catch false) + then $normalized else error("invalid timestamp") end' 2>/dev/null +} + +timestamp_epoch() { + jq -nr --arg timestamp "$1" '$timestamp | fromdateiso8601' +} + +add_seconds() { + jq -nr --arg timestamp "$1" --argjson seconds "$2" \ + '$timestamp | fromdateiso8601 + $seconds | todateiso8601' +} + +emit_null() { + local opportunity_key=$1 case_json=$2 evidence_cutoff=$3 matures_at=$4 reason=$5 + jq -cn --arg opportunityKey "$opportunity_key" --argjson case "$case_json" \ + --arg evidenceCutoff "$evidence_cutoff" --arg maturesAt "$matures_at" --arg reason "$reason" \ + '{value:null, opportunityKey:$opportunityKey, case:$case, evidenceCutoff:$evidenceCutoff, + maturesAt:$maturesAt, provenance:[], diagnostics:{missingReason:$reason}}' +} + +find_reports() { + local run_id=$1 sha=$2 cutoff=$3 response + response=$(gh api -X GET search/issues \ + -f q="repo:$REPOSITORY is:issue in:body \"id: $run_id\"" -f per_page=100 2>"$tmp_dir/search-error") \ + || return 1 + if ! printf '%s\n' "$response" | jq -e '.incomplete_results == false and (.items | type == "array")' >/dev/null; then + return 1 + fi + if [[ $(printf '%s\n' "$response" | jq '[.items[] | select(.title | startswith("[duplicate-code] Duplicate Code:"))] | length') -eq 0 ]]; then + response=$(gh api -X GET search/issues \ + -f q="repo:$REPOSITORY is:issue in:body \"Analysis of commit\" \"$sha\"" -f per_page=100 \ + 2>"$tmp_dir/search-fallback-error") || return 1 + fi + printf '%s\n' "$response" | jq -cer --arg cutoff "$cutoff" ' + select(.incomplete_results == false) + | [.items[] + | select(.title | startswith("[duplicate-code] Duplicate Code:")) + | select(.created_at <= $cutoff) + | {number, createdAt:.created_at}] + | unique_by(.number)' +} + +report_merged_by_cutoff() { + local number=$1 cutoff=$2 response + response=$(gh api graphql -F owner=github -F name=gh-aw -F number="$number" \ + -f query='query($owner: String!, $name: String!, $number: Int!) { + repository(owner: $owner, name: $name) { + issue(number: $number) { + closedByPullRequestsReferences(first: 20) { + nodes { mergedAt } + } + } + } + }' 2>"$tmp_dir/issue-$number-error") || return 1 + printf '%s\n' "$response" | jq -e --arg cutoff "$cutoff" ' + [.data.repository.issue.closedByPullRequestsReferences.nodes[]?.mergedAt + | select(type == "string" and . <= $cutoff)] | length > 0' >/dev/null +} + +grade_run() { + local request run_id repository workflow run_sha created_at evidence_at matures_at evidence_cutoff + local evidence_epoch matures_epoch case_json reports report_numbers merged_reports evidence value provenance + request=$(cat) + if ! printf '%s\n' "$request" | jq -e ' + .schemaVersion == 1 and (.run.id | type == "string" and length > 0) + and (.run.repository | type == "string") and (.run.workflow | type == "string") + and (.run.sha | type == "string" and test("^[0-9a-f]{40}$")) + and (.run.createdAt | type == "string") and (.evidenceAt | type == "string") + and (.case == null or (.case | type == "object"))' >/dev/null 2>&1; then + emit_null invalid-request '{"invalidRequest":true}' 1970-01-01T00:00:00Z 1970-01-01T00:00:00Z "invalid request" + return + fi + run_id=$(printf '%s\n' "$request" | jq -r '.run.id') + repository=$(printf '%s\n' "$request" | jq -r '.run.repository') + workflow=$(printf '%s\n' "$request" | jq -r '.run.workflow') + run_sha=$(printf '%s\n' "$request" | jq -r '.run.sha') + created_at=$(printf '%s\n' "$request" | jq -r '.run.createdAt') + evidence_at=$(printf '%s\n' "$request" | jq -r '.evidenceAt') + if ! created_at=$(normalize_timestamp "$created_at") || ! evidence_at=$(normalize_timestamp "$evidence_at"); then + emit_null invalid-timestamp '{"invalidTimestamp":true}' 1970-01-01T00:00:00Z 1970-01-01T00:00:00Z "invalid timestamp" + return + fi + matures_at=$(add_seconds "$created_at" "$MATURATION_SECONDS") + evidence_epoch=$(timestamp_epoch "$evidence_at") + matures_epoch=$(timestamp_epoch "$matures_at") + if (( evidence_epoch < matures_epoch )); then evidence_cutoff=$evidence_at; else evidence_cutoff=$matures_at; fi + case_json=$(jq -cn --arg runId "$run_id" --arg subjectSha "$run_sha" \ + '{runId:$runId, subjectSha:$subjectSha}') + if [[ $repository != "$REPOSITORY" || $workflow != "$WORKFLOW_NAME" ]]; then + emit_null "run:$run_id" "$case_json" "$evidence_cutoff" "$matures_at" "run repository or workflow does not match the frozen contract" + return + fi + if ! reports=$(find_reports "$run_id" "$run_sha" "$evidence_cutoff"); then + emit_null "run:$run_id" "$case_json" "$evidence_cutoff" "$matures_at" "report-search-unavailable" + return + fi + report_numbers=$(printf '%s\n' "$reports" | jq -c '[.[].number]') + if [[ $report_numbers == '[]' ]]; then + emit_null "run:$run_id" "$case_json" "$evidence_cutoff" "$matures_at" "no-qualifying-reports" + return + fi + merged_reports=0 + while IFS= read -r number; do + if report_merged_by_cutoff "$number" "$evidence_cutoff"; then merged_reports=$((merged_reports + 1)); fi + done < <(printf '%s\n' "$report_numbers" | jq -r '.[]') + evidence=$(jq -cn --argjson qualifyingReports "$(printf '%s\n' "$report_numbers" | jq length)" \ + --argjson mergedReports "$merged_reports" '{valid:true, qualifyingReports:$qualifyingReports, mergedReports:$mergedReports}') + value=$(printf '%s\n' "$evidence" | metric) + provenance=$(printf '%s\n' "$report_numbers" | jq -c --arg repository "$repository" \ + '[.[] | {repository:$repository, kind:"issue", ref:(tostring)}]') + jq -cn --argjson value "$value" --arg opportunityKey "run:$run_id" --argjson case "$case_json" \ + --arg evidenceCutoff "$evidence_cutoff" --arg maturesAt "$matures_at" --argjson provenance "$provenance" \ + '{value:$value, opportunityKey:$opportunityKey, case:$case, evidenceCutoff:$evidenceCutoff, + maturesAt:$maturesAt, provenance:$provenance}' +} + +case ${1:-} in + --definition) definition ;; + --metric) metric ;; + --grade-run) grade_run ;; + *) printf 'usage: %s --definition|--metric|--grade-run\n' "$0" >&2; exit 1 ;; +esac diff --git a/.github/workflows/duplicate-code-detector.lock.yml b/.github/workflows/duplicate-code-detector.lock.yml index b3b61cedd07..5f0e91e43e6 100644 --- a/.github/workflows/duplicate-code-detector.lock.yml +++ b/.github/workflows/duplicate-code-detector.lock.yml @@ -1,4 +1,4 @@ -# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"86440c3dd53a5fe4632f4f021b42703ec7028648e8f9086e606427544dfa40d9","body_hash":"4e290bfde2682a1a8936e7575c807cd00918f494ef36d9843f78cb2426ff7935","strict":true,"agent_id":"codex","agent_model":"copilot/auto","engine_versions":{"codex":"0.150.1"}} +# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"dc87d3a6d9981a2dd0d243a0b7f9f618271960cac527e3ff74e64df141eed44a","body_hash":"4e290bfde2682a1a8936e7575c807cd00918f494ef36d9843f78cb2426ff7935","strict":true,"agent_id":"codex","agent_model":"copilot/auto","engine_versions":{"codex":"0.150.1"}} # gh-aw-manifest: {"version":1,"secrets":["CODEX_API_KEY","GH_AW_AGENT_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GH_AW_OTEL_GRAFANA_AUTHORIZATION","GH_AW_OTEL_GRAFANA_ENDPOINT","GH_AW_OTEL_SENTRY_AUTHORIZATION","GH_AW_OTEL_SENTRY_ENDPOINT","GITHUB_TOKEN","OPENAI_API_KEY"],"actions":[{"repo":"actions/cache/restore","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/cache/save","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/checkout","sha":"3d3c42e5aac5ba805825da76410c181273ba90b1","version":"v7.0.1"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-go","sha":"b7ad1dad31e06c5925ef5d2fc7ad053ef454303e","version":"v7.0.0"},{"repo":"actions/setup-node","sha":"820762786026740c76f36085b0efc47a31fe5020","version":"v7.0.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.28.13","digest":"sha256:78aeb1eee876d1ebae5eef26402bd9f31112850d42016a240002d1da57203e15","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.28.13@sha256:78aeb1eee876d1ebae5eef26402bd9f31112850d42016a240002d1da57203e15"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.28.13","digest":"sha256:61f12dcff38eb008b645d87a5541763452f409d9040eb243718764944167b1a2","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.28.13@sha256:61f12dcff38eb008b645d87a5541763452f409d9040eb243718764944167b1a2"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.28.13","digest":"sha256:58a8dc5be3fbafeef7da0532c38bd9047f86bf1b3c4cfd7d9b7d8d28e80a2b56","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.28.13@sha256:58a8dc5be3fbafeef7da0532c38bd9047f86bf1b3c4cfd7d9b7d8d28e80a2b56"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.4.15","digest":"sha256:60cd97533e93d8e7be36b979c0f08a70846189bda6190f28bbd6d427bc0d9b6e","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.4.15@sha256:60cd97533e93d8e7be36b979c0f08a70846189bda6190f28bbd6d427bc0d9b6e"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:bac2192f6374d6262116399b34fc5e143d576f82719e90a18261cae7480f4d4e","pinned_image":"ghcr.io/github/gh-aw-node@sha256:bac2192f6374d6262116399b34fc5e143d576f82719e90a18261cae7480f4d4e"},{"image":"ghcr.io/github/github-mcp-server:v1.11.0","digest":"sha256:fbec75de11c255213fa08d80fb166abe73d851fff631c51c0079872967720699","pinned_image":"ghcr.io/github/github-mcp-server:v1.11.0@sha256:fbec75de11c255213fa08d80fb166abe73d851fff631c51c0079872967720699"},{"image":"ghcr.io/oraios/serena:1.7.0","digest":"sha256:6c9459e4246a39c9deaa4f23fb05a526ac6e237b24c8e84a927a098fa1ab6730","pinned_image":"ghcr.io/oraios/serena:1.7.0@sha256:6c9459e4246a39c9deaa4f23fb05a526ac6e237b24c8e84a927a098fa1ab6730"}],"mcp_servers":[{"name":"github","tools":["get_commit","get_file_contents","get_latest_release","get_me","get_pull_request","get_pull_request_comments","get_pull_request_diff","get_pull_request_files","get_pull_request_review_comments","get_pull_request_reviews","get_pull_request_status","get_release_by_tag","get_tag","issue_read","list_branches","list_commits","list_issue_types","list_issues","list_pull_requests","list_releases","list_starred_repositories","list_tags","pull_request_read","search_code","search_issues","search_pull_requests","search_repositories"]},{"name":"safeoutputs","tools":["create_issue","missing_data","missing_tool","noop"]},{"name":"serena","tools":["*"]}]} # This file was automatically generated by gh-aw. DO NOT EDIT. To debug this workflow, load the skill at https://github.com/github/gh-aw/blob/main/debug.md # @@ -113,6 +113,7 @@ jobs: lockdown_check_failed: ${{ steps.generate_aw_info.outputs.lockdown_check_failed == 'true' }} model: ${{ steps.generate_aw_info.outputs.model }} oauth_token_check_failed: ${{ steps.check-oauth-tokens.outputs.oauth_token_check_failed == 'true' }} + run_created_at: ${{ steps.generate_aw_info.outputs.run_created_at }} setup-parent-span-id: ${{ steps.setup.outputs.parent-span-id || steps.setup.outputs.span-id }} setup-span-id: ${{ steps.setup.outputs.span-id }} setup-trace-id: ${{ steps.setup.outputs.trace-id }} @@ -159,6 +160,7 @@ jobs: GH_AW_INFO_AWMG_VERSION: "" GH_AW_INFO_FIREWALL_TYPE: "squid" GH_AW_INFO_AGENT_RUNTIME: "cloud-hypervisor" + GH_AW_INFO_FETCH_RUN_CREATED_AT: "true" GH_AW_INFO_FRONTMATTER_EMOJI: "🔍" GH_AW_COMPILED_STRICT: "true" GH_AW_INFO_FEATURES: '{"gh-aw-detection":true}' @@ -1178,6 +1180,38 @@ jobs: setupGlobals(core, github, context, exec, io, getOctokit); const { main } = require(path.join(actionsDir, 'generate_observability_summary.cjs')); await main(core); + - name: Run graders + if: always() + continue-on-error: true + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + with: + script: | + const path = require('path'); + const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); + const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); + setupGlobals(core, github, context, exec, io, getOctokit); + const { main } = require(path.join(actionsDir, 'trace_graders.cjs')); + await main('eyJ2ZXJzaW9uIjoxLCJncmFkZXJzIjpbeyJpZCI6Im9wZXJhdGlvbmFsLXZhbHVlIiwibmFtZSI6Ik9wZXJhdGlvbmFsIFZhbHVlIiwic291cmNlIjoib3BlcmF0aW9uYWwtdmFsdWUiLCJlbmFibGVkIjp0cnVlLCJ1bml0IjoicmF0aW8iLCJkaXJlY3Rpb24iOiJoaWdoZXJfaXNfYmV0dGVyIiwibWF4IjoxLCJtaW4iOjAsImRpZ2VzdCI6ImM5ODAyMzk0ZmM3OGEyNjM5NTE0Njk5NDcwYWFiMTk1ZDhhMmViNTg4MmU2Y2EwYTUwMjdkNmUzZmUzNTM5MDIiLCJydW4iOiIuZ2l0aHViL2dyYWRlcnMvZHVwbGljYXRlLWNvZGUtZGV0ZWN0b3Itb3BlcmF0aW9uYWwtdmFsdWUuc2gifV19', 'W3siaWQiOiJvcGVyYXRpb25hbC12YWx1ZSIsInJ1biI6IiMhL3Vzci9iaW4vZW52IGJhc2hcblxuc2V0IC1ldW8gcGlwZWZhaWxcblxuUkVQT1NJVE9SWT1naXRodWIvZ2gtYXdcbldPUktGTE9XX05BTUU9XCJEdXBsaWNhdGUgQ29kZSBEZXRlY3RvclwiXG5NQVRVUkFUSU9OX1NFQ09ORFM9MTIwOTYwMFxuXG50bXBfZGlyPSQobWt0ZW1wIC1kIFwiJHtUTVBESVI6LS90bXB9L2R1cGxpY2F0ZS1jb2RlLWRldGVjdG9yLW9wZXJhdGlvbmFsLXZhbHVlLlhYWFhYWFwiKVxudHJhcCAncm0gLXJmIFwiJHRtcF9kaXJcIicgRVhJVCBIVVAgSU5UIFRFUk1cblxuZGVmaW5pdGlvbigpIHtcbiAgICBjYXQgXHUwMDNjXHUwMDNjJ0pTT04nXG57XG4gIFwic2NoZW1hVmVyc2lvblwiOiA0LCBcImdyYWRlclwiOiBcIm9wZXJhdGlvbmFsLXZhbHVlXCIsXG4gIFwicmVwb3NpdG9yeVwiOiBcImdpdGh1Yi9naC1hd1wiLCBcIndvcmtmbG93TmFtZVwiOiBcIkR1cGxpY2F0ZSBDb2RlIERldGVjdG9yXCIsXG4gIFwic291cmNlUGF0aFwiOiBcIi5naXRodWIvd29ya2Zsb3dzL2R1cGxpY2F0ZS1jb2RlLWRldGVjdG9yLm1kXCIsXG4gIFwiYWRvcHRpb25cIjoge1wiY29tbWl0XCI6IFwiYzNjMDBkMjk3ZTA3MGNkN2Y3NjZkMmM4N2JiODA5MzAxOTEyZTg3YlwiLCBcImFkb3B0ZWRBdFwiOiBcIjIwMjUtMTAtMDdUMDY6Mjk6NTBaXCJ9LFxuICBcIm9wZXJhdGlvbmFsVmFsdWVcIjogXCJSZXNvbHZlIHRoZSBkdXBsaWNhdGUtY29kZSByZXBvcnRzIGFzc2lnbmVkIHRvIGEgZGV0ZWN0b3IgcnVuIHRocm91Z2ggbWVyZ2VkIHB1bGwgcmVxdWVzdHMuXCIsXG4gIFwiZXZpZGVuY2VcIjoge1xuICAgIFwib3Bwb3J0dW5pdHlcIjogXCJUaGUgZGlzdGluY3QgZHVwbGljYXRlLWNvZGUgcmVwb3J0cyB3aG9zZSBnZW5lcmF0ZWQgYm9keSBiaW5kcyB0aGVtIHRvIHRoZSBkZXRlY3RvciBydW4gSUQsIG9yIGZvciBsZWdhY3kgcnVucyBpdHMgYW5hbHl6ZWQgY29tbWl0LlwiLFxuICAgIFwiYXNzaWdubWVudFwiOiBcIlJ1biBJRCBpcyBwcmltYXJ5IGFuZCBhbmFseXplZCBjb21taXQgU0hBIGlzIHRoZSBoaXN0b3JpY2FsIGZhbGxiYWNrOyBvbmx5ICdbZHVwbGljYXRlLWNvZGVdIER1cGxpY2F0ZSBDb2RlOicgaXNzdWVzIHF1YWxpZnkuIFRoZSBydW4ga2V5IHJlcGVhdHMgZm9yIHJlcnVucy5cIixcbiAgICBcImFjY2VwdGVkXCI6IFwiR2l0SHViIGlzc3VlIGJvZGllcyBwcm92aW5nIGFzc2lnbm1lbnQgYW5kIHRoZWlyIGNsb3NpbmcgcHVsbCByZXF1ZXN0cycgbWVyZ2VkIHN0YXRlIGFuZCB0aW1lLiBBZ2VudCBvdXRwdXQsIHRyYWNlcywgaXNzdWUgY2xvc3VyZSBhbG9uZSwgYW5kIHJlcG9ydCB2b2x1bWUgYXJlIGV4Y2x1ZGVkLlwiLFxuICAgIFwicmVwb3NpdG9yaWVzXCI6IFtcImdpdGh1Yi9naC1hd1wiXSxcbiAgICBcImNvbGxlY3Rpb25cIjogXCJXaXRoIGlzc3VlczpyZWFkIGFuZCBwdWxsLXJlcXVlc3RzOnJlYWQsIHNlYXJjaCBpc3N1ZSBib2RpZXMgYXQgdGhlIGNhcHBlZCBjdXRvZmYgYW5kIHF1ZXJ5IGVhY2ggcXVhbGlmeWluZyBpc3N1ZSdzIGNsb3NpbmcgcHVsbCByZXF1ZXN0cy5cIixcbiAgICBcIm1hdHVyYXRpb25cIjogXCJGb3VydGVlbiBkYXlzLiBDb21wYXJhYmxlIHByZS1hZG9wdGlvbiBkdXBsaWNhdGUtcmVwb3J0IGV2aWRlbmNlIGRvZXMgbm90IGV4aXN0IGJlY2F1c2UgdGhpcyB3b3JrZmxvdyBpbnRyb2R1Y2VkIHRoZSByZXBvcnQgZm9ybWF0LlwiLFxuICAgIFwiemVyb1J1bGVcIjogXCJBIHF1YWxpZnlpbmcgcmVwb3J0IHdpdGhvdXQgYSBjbG9zaW5nIHB1bGwgcmVxdWVzdCBtZXJnZWQgYnkgdGhlIGN1dG9mZiBjb250cmlidXRlcyAwLlwiLFxuICAgIFwibWlzc2luZ1J1bGVcIjogXCJObyBxdWFsaWZ5aW5nIHJlcG9ydCwgaW5jb21wbGV0ZSBzZWFyY2ggcmVzdWx0cywgdW5hdmFpbGFibGUgaXNzdWUgZXZpZGVuY2UsIG9yIG1hbGZvcm1lZCBldmlkZW5jZSBzY29yZXMgbnVsbCwgbmV2ZXIgMC5cIlxuICB9LFxuICBcInByaW1hcnlNZXRyaWNcIjoge1wiaWRcIjogXCJkdXBsaWNhdGUtcmVwb3J0LXJlbWVkaWF0aW9uLXJhdGVcIiwgXCJmb3JtdWxhXCI6IFwibWVyZ2VkUmVwb3J0cyAvIHF1YWxpZnlpbmdSZXBvcnRzLCB3aGVyZSBhIHJlcG9ydCBpcyBhdHRhaW5lZCBvbmx5IHdoZW4gYSBjbG9zaW5nIHB1bGwgcmVxdWVzdCBtZXJnZWQgbm8gbGF0ZXIgdGhhbiB0aGUgY2FwcGVkIGN1dG9mZi5cIiwgXCJkaXJlY3Rpb25cIjogXCJoaWdoZXJfaXNfYmV0dGVyXCJ9LFxuICBcImJhc2VsaW5lXCI6IHtcIm1vZGVcIjogXCJhdHRhaW5tZW50LW9ubHlcIiwgXCJ2YWx1ZVwiOiBudWxsLCBcImV2aWRlbmNlQ3V0b2ZmXCI6IG51bGwsIFwicHJvdmVuYW5jZVwiOiBbXX0sXG4gIFwidmFsaWRhdGlvbkV4YW1wbGVzXCI6IHtcbiAgICBcInRhcmdldEF0dGFpbmVkXCI6IHtcInZhbGlkXCI6dHJ1ZSxcInF1YWxpZnlpbmdSZXBvcnRzXCI6MixcIm1lcmdlZFJlcG9ydHNcIjoyfSxcbiAgICBcInRhcmdldE1pc3NlZFwiOiB7XCJ2YWxpZFwiOnRydWUsXCJxdWFsaWZ5aW5nUmVwb3J0c1wiOjIsXCJtZXJnZWRSZXBvcnRzXCI6MH0sXG4gICAgXCJtaXNzaW5nXCI6IHtcInZhbGlkXCI6ZmFsc2V9LFxuICAgIFwibWFsZm9ybWVkXCI6IHtcInZhbGlkXCI6XCJ5ZXNcIixcInF1YWxpZnlpbmdSZXBvcnRzXCI6XCIyXCIsXCJtZXJnZWRSZXBvcnRzXCI6Mn1cbiAgfVxufVxuSlNPTlxufVxuXG5tZXRyaWMoKSB7XG4gICAgbG9jYWwgZXZpZGVuY2VcbiAgICBldmlkZW5jZT0kKGNhdClcbiAgICBpZiAhIHByaW50ZiAnJXNcXG4nIFwiJGV2aWRlbmNlXCIgfCBqcSAtZSAuIFx1MDAzZS9kZXYvbnVsbCAyXHUwMDNlXHUwMDI2MTsgdGhlblxuICAgICAgICBwcmludGYgJ251bGxcXG4nXG4gICAgICAgIHJldHVyblxuICAgIGZpXG4gICAgcHJpbnRmICclc1xcbicgXCIkZXZpZGVuY2VcIiB8IGpxICdcbiAgICAgIGlmIC52YWxpZCAhPSB0cnVlXG4gICAgICAgIG9yIChbLnF1YWxpZnlpbmdSZXBvcnRzLC5tZXJnZWRSZXBvcnRzXSB8IGFsbCguW107IHR5cGUgPT0gXCJudW1iZXJcIiBhbmQgZmxvb3IgPT0gLikgfCBub3QpXG4gICAgICAgIG9yIC5xdWFsaWZ5aW5nUmVwb3J0cyBcdTAwM2M9IDAgb3IgLm1lcmdlZFJlcG9ydHMgXHUwMDNjIDAgb3IgLm1lcmdlZFJlcG9ydHMgXHUwMDNlIC5xdWFsaWZ5aW5nUmVwb3J0c1xuICAgICAgdGhlbiBudWxsIGVsc2UgLm1lcmdlZFJlcG9ydHMgLyAucXVhbGlmeWluZ1JlcG9ydHMgZW5kJ1xufVxuXG5ub3JtYWxpemVfdGltZXN0YW1wKCkge1xuICAgIGpxIC1uciAtLWFyZyB0aW1lc3RhbXAgXCIkMVwiICdcbiAgICAgICgkdGltZXN0YW1wIHwgc3ViKFwiXFxcXC5bMC05XStaJFwiOyBcIlpcIikpIGFzICRub3JtYWxpemVkXG4gICAgICB8IGlmICgkbm9ybWFsaXplZCB8IHRlc3QoXCJeWzAtOV17NH0tWzAtOV17Mn0tWzAtOV17Mn1UWzAtOV17Mn06WzAtOV17Mn06WzAtOV17Mn1aJFwiKSlcbiAgICAgICAgYW5kICh0cnkgKCgkbm9ybWFsaXplZCB8IGZyb21kYXRlaXNvODYwMSB8IHRvZGF0ZWlzbzg2MDEpID09ICRub3JtYWxpemVkKSBjYXRjaCBmYWxzZSlcbiAgICAgICAgdGhlbiAkbm9ybWFsaXplZCBlbHNlIGVycm9yKFwiaW52YWxpZCB0aW1lc3RhbXBcIikgZW5kJyAyXHUwMDNlL2Rldi9udWxsXG59XG5cbnRpbWVzdGFtcF9lcG9jaCgpIHtcbiAgICBqcSAtbnIgLS1hcmcgdGltZXN0YW1wIFwiJDFcIiAnJHRpbWVzdGFtcCB8IGZyb21kYXRlaXNvODYwMSdcbn1cblxuYWRkX3NlY29uZHMoKSB7XG4gICAganEgLW5yIC0tYXJnIHRpbWVzdGFtcCBcIiQxXCIgLS1hcmdqc29uIHNlY29uZHMgXCIkMlwiIFxcXG4gICAgICAgICckdGltZXN0YW1wIHwgZnJvbWRhdGVpc284NjAxICsgJHNlY29uZHMgfCB0b2RhdGVpc284NjAxJ1xufVxuXG5lbWl0X251bGwoKSB7XG4gICAgbG9jYWwgb3Bwb3J0dW5pdHlfa2V5PSQxIGNhc2VfanNvbj0kMiBldmlkZW5jZV9jdXRvZmY9JDMgbWF0dXJlc19hdD0kNCByZWFzb249JDVcbiAgICBqcSAtY24gLS1hcmcgb3Bwb3J0dW5pdHlLZXkgXCIkb3Bwb3J0dW5pdHlfa2V5XCIgLS1hcmdqc29uIGNhc2UgXCIkY2FzZV9qc29uXCIgXFxcbiAgICAgICAgLS1hcmcgZXZpZGVuY2VDdXRvZmYgXCIkZXZpZGVuY2VfY3V0b2ZmXCIgLS1hcmcgbWF0dXJlc0F0IFwiJG1hdHVyZXNfYXRcIiAtLWFyZyByZWFzb24gXCIkcmVhc29uXCIgXFxcbiAgICAgICAgJ3t2YWx1ZTpudWxsLCBvcHBvcnR1bml0eUtleTokb3Bwb3J0dW5pdHlLZXksIGNhc2U6JGNhc2UsIGV2aWRlbmNlQ3V0b2ZmOiRldmlkZW5jZUN1dG9mZixcbiAgICAgICAgICBtYXR1cmVzQXQ6JG1hdHVyZXNBdCwgcHJvdmVuYW5jZTpbXSwgZGlhZ25vc3RpY3M6e21pc3NpbmdSZWFzb246JHJlYXNvbn19J1xufVxuXG5maW5kX3JlcG9ydHMoKSB7XG4gICAgbG9jYWwgcnVuX2lkPSQxIHNoYT0kMiBjdXRvZmY9JDMgcmVzcG9uc2VcbiAgICByZXNwb25zZT0kKGdoIGFwaSAtWCBHRVQgc2VhcmNoL2lzc3VlcyBcXFxuICAgICAgICAtZiBxPVwicmVwbzokUkVQT1NJVE9SWSBpczppc3N1ZSBpbjpib2R5IFxcXCJpZDogJHJ1bl9pZFxcXCJcIiAtZiBwZXJfcGFnZT0xMDAgMlx1MDAzZVwiJHRtcF9kaXIvc2VhcmNoLWVycm9yXCIpIFxcXG4gICAgICAgIHx8IHJldHVybiAxXG4gICAgaWYgISBwcmludGYgJyVzXFxuJyBcIiRyZXNwb25zZVwiIHwganEgLWUgJy5pbmNvbXBsZXRlX3Jlc3VsdHMgPT0gZmFsc2UgYW5kICguaXRlbXMgfCB0eXBlID09IFwiYXJyYXlcIiknIFx1MDAzZS9kZXYvbnVsbDsgdGhlblxuICAgICAgICByZXR1cm4gMVxuICAgIGZpXG4gICAgaWYgW1sgJChwcmludGYgJyVzXFxuJyBcIiRyZXNwb25zZVwiIHwganEgJ1suaXRlbXNbXSB8IHNlbGVjdCgudGl0bGUgfCBzdGFydHN3aXRoKFwiW2R1cGxpY2F0ZS1jb2RlXSBEdXBsaWNhdGUgQ29kZTpcIikpXSB8IGxlbmd0aCcpIC1lcSAwIF1dOyB0aGVuXG4gICAgICAgIHJlc3BvbnNlPSQoZ2ggYXBpIC1YIEdFVCBzZWFyY2gvaXNzdWVzIFxcXG4gICAgICAgICAgICAtZiBxPVwicmVwbzokUkVQT1NJVE9SWSBpczppc3N1ZSBpbjpib2R5IFxcXCJBbmFseXNpcyBvZiBjb21taXRcXFwiIFxcXCIkc2hhXFxcIlwiIC1mIHBlcl9wYWdlPTEwMCBcXFxuICAgICAgICAgICAgMlx1MDAzZVwiJHRtcF9kaXIvc2VhcmNoLWZhbGxiYWNrLWVycm9yXCIpIHx8IHJldHVybiAxXG4gICAgZmlcbiAgICBwcmludGYgJyVzXFxuJyBcIiRyZXNwb25zZVwiIHwganEgLWNlciAtLWFyZyBjdXRvZmYgXCIkY3V0b2ZmXCIgJ1xuICAgICAgc2VsZWN0KC5pbmNvbXBsZXRlX3Jlc3VsdHMgPT0gZmFsc2UpXG4gICAgICB8IFsuaXRlbXNbXVxuICAgICAgICAgfCBzZWxlY3QoLnRpdGxlIHwgc3RhcnRzd2l0aChcIltkdXBsaWNhdGUtY29kZV0gRHVwbGljYXRlIENvZGU6XCIpKVxuICAgICAgICAgfCBzZWxlY3QoLmNyZWF0ZWRfYXQgXHUwMDNjPSAkY3V0b2ZmKVxuICAgICAgICAgfCB7bnVtYmVyLCBjcmVhdGVkQXQ6LmNyZWF0ZWRfYXR9XVxuICAgICAgfCB1bmlxdWVfYnkoLm51bWJlciknXG59XG5cbnJlcG9ydF9tZXJnZWRfYnlfY3V0b2ZmKCkge1xuICAgIGxvY2FsIG51bWJlcj0kMSBjdXRvZmY9JDIgcmVzcG9uc2VcbiAgICByZXNwb25zZT0kKGdoIGFwaSBncmFwaHFsIC1GIG93bmVyPWdpdGh1YiAtRiBuYW1lPWdoLWF3IC1GIG51bWJlcj1cIiRudW1iZXJcIiBcXFxuICAgICAgICAtZiBxdWVyeT0ncXVlcnkoJG93bmVyOiBTdHJpbmchLCAkbmFtZTogU3RyaW5nISwgJG51bWJlcjogSW50ISkge1xuICAgICAgICAgIHJlcG9zaXRvcnkob3duZXI6ICRvd25lciwgbmFtZTogJG5hbWUpIHtcbiAgICAgICAgICAgIGlzc3VlKG51bWJlcjogJG51bWJlcikge1xuICAgICAgICAgICAgICBjbG9zZWRCeVB1bGxSZXF1ZXN0c1JlZmVyZW5jZXMoZmlyc3Q6IDIwKSB7XG4gICAgICAgICAgICAgICAgbm9kZXMgeyBtZXJnZWRBdCB9XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9XG4gICAgICAgIH0nIDJcdTAwM2VcIiR0bXBfZGlyL2lzc3VlLSRudW1iZXItZXJyb3JcIikgfHwgcmV0dXJuIDFcbiAgICBwcmludGYgJyVzXFxuJyBcIiRyZXNwb25zZVwiIHwganEgLWUgLS1hcmcgY3V0b2ZmIFwiJGN1dG9mZlwiICdcbiAgICAgIFsuZGF0YS5yZXBvc2l0b3J5Lmlzc3VlLmNsb3NlZEJ5UHVsbFJlcXVlc3RzUmVmZXJlbmNlcy5ub2Rlc1tdPy5tZXJnZWRBdFxuICAgICAgIHwgc2VsZWN0KHR5cGUgPT0gXCJzdHJpbmdcIiBhbmQgLiBcdTAwM2M9ICRjdXRvZmYpXSB8IGxlbmd0aCBcdTAwM2UgMCcgXHUwMDNlL2Rldi9udWxsXG59XG5cbmdyYWRlX3J1bigpIHtcbiAgICBsb2NhbCByZXF1ZXN0IHJ1bl9pZCByZXBvc2l0b3J5IHdvcmtmbG93IHJ1bl9zaGEgY3JlYXRlZF9hdCBldmlkZW5jZV9hdCBtYXR1cmVzX2F0IGV2aWRlbmNlX2N1dG9mZlxuICAgIGxvY2FsIGV2aWRlbmNlX2Vwb2NoIG1hdHVyZXNfZXBvY2ggY2FzZV9qc29uIHJlcG9ydHMgcmVwb3J0X251bWJlcnMgbWVyZ2VkX3JlcG9ydHMgZXZpZGVuY2UgdmFsdWUgcHJvdmVuYW5jZVxuICAgIHJlcXVlc3Q9JChjYXQpXG4gICAgaWYgISBwcmludGYgJyVzXFxuJyBcIiRyZXF1ZXN0XCIgfCBqcSAtZSAnXG4gICAgICAuc2NoZW1hVmVyc2lvbiA9PSAxIGFuZCAoLnJ1bi5pZCB8IHR5cGUgPT0gXCJzdHJpbmdcIiBhbmQgbGVuZ3RoIFx1MDAzZSAwKVxuICAgICAgYW5kICgucnVuLnJlcG9zaXRvcnkgfCB0eXBlID09IFwic3RyaW5nXCIpIGFuZCAoLnJ1bi53b3JrZmxvdyB8IHR5cGUgPT0gXCJzdHJpbmdcIilcbiAgICAgIGFuZCAoLnJ1bi5zaGEgfCB0eXBlID09IFwic3RyaW5nXCIgYW5kIHRlc3QoXCJeWzAtOWEtZl17NDB9JFwiKSlcbiAgICAgIGFuZCAoLnJ1bi5jcmVhdGVkQXQgfCB0eXBlID09IFwic3RyaW5nXCIpIGFuZCAoLmV2aWRlbmNlQXQgfCB0eXBlID09IFwic3RyaW5nXCIpXG4gICAgICBhbmQgKC5jYXNlID09IG51bGwgb3IgKC5jYXNlIHwgdHlwZSA9PSBcIm9iamVjdFwiKSknIFx1MDAzZS9kZXYvbnVsbCAyXHUwMDNlXHUwMDI2MTsgdGhlblxuICAgICAgICBlbWl0X251bGwgaW52YWxpZC1yZXF1ZXN0ICd7XCJpbnZhbGlkUmVxdWVzdFwiOnRydWV9JyAxOTcwLTAxLTAxVDAwOjAwOjAwWiAxOTcwLTAxLTAxVDAwOjAwOjAwWiBcImludmFsaWQgcmVxdWVzdFwiXG4gICAgICAgIHJldHVyblxuICAgIGZpXG4gICAgcnVuX2lkPSQocHJpbnRmICclc1xcbicgXCIkcmVxdWVzdFwiIHwganEgLXIgJy5ydW4uaWQnKVxuICAgIHJlcG9zaXRvcnk9JChwcmludGYgJyVzXFxuJyBcIiRyZXF1ZXN0XCIgfCBqcSAtciAnLnJ1bi5yZXBvc2l0b3J5JylcbiAgICB3b3JrZmxvdz0kKHByaW50ZiAnJXNcXG4nIFwiJHJlcXVlc3RcIiB8IGpxIC1yICcucnVuLndvcmtmbG93JylcbiAgICBydW5fc2hhPSQocHJpbnRmICclc1xcbicgXCIkcmVxdWVzdFwiIHwganEgLXIgJy5ydW4uc2hhJylcbiAgICBjcmVhdGVkX2F0PSQocHJpbnRmICclc1xcbicgXCIkcmVxdWVzdFwiIHwganEgLXIgJy5ydW4uY3JlYXRlZEF0JylcbiAgICBldmlkZW5jZV9hdD0kKHByaW50ZiAnJXNcXG4nIFwiJHJlcXVlc3RcIiB8IGpxIC1yICcuZXZpZGVuY2VBdCcpXG4gICAgaWYgISBjcmVhdGVkX2F0PSQobm9ybWFsaXplX3RpbWVzdGFtcCBcIiRjcmVhdGVkX2F0XCIpIHx8ICEgZXZpZGVuY2VfYXQ9JChub3JtYWxpemVfdGltZXN0YW1wIFwiJGV2aWRlbmNlX2F0XCIpOyB0aGVuXG4gICAgICAgIGVtaXRfbnVsbCBpbnZhbGlkLXRpbWVzdGFtcCAne1wiaW52YWxpZFRpbWVzdGFtcFwiOnRydWV9JyAxOTcwLTAxLTAxVDAwOjAwOjAwWiAxOTcwLTAxLTAxVDAwOjAwOjAwWiBcImludmFsaWQgdGltZXN0YW1wXCJcbiAgICAgICAgcmV0dXJuXG4gICAgZmlcbiAgICBtYXR1cmVzX2F0PSQoYWRkX3NlY29uZHMgXCIkY3JlYXRlZF9hdFwiIFwiJE1BVFVSQVRJT05fU0VDT05EU1wiKVxuICAgIGV2aWRlbmNlX2Vwb2NoPSQodGltZXN0YW1wX2Vwb2NoIFwiJGV2aWRlbmNlX2F0XCIpXG4gICAgbWF0dXJlc19lcG9jaD0kKHRpbWVzdGFtcF9lcG9jaCBcIiRtYXR1cmVzX2F0XCIpXG4gICAgaWYgKCggZXZpZGVuY2VfZXBvY2ggXHUwMDNjIG1hdHVyZXNfZXBvY2ggKSk7IHRoZW4gZXZpZGVuY2VfY3V0b2ZmPSRldmlkZW5jZV9hdDsgZWxzZSBldmlkZW5jZV9jdXRvZmY9JG1hdHVyZXNfYXQ7IGZpXG4gICAgY2FzZV9qc29uPSQoanEgLWNuIC0tYXJnIHJ1bklkIFwiJHJ1bl9pZFwiIC0tYXJnIHN1YmplY3RTaGEgXCIkcnVuX3NoYVwiIFxcXG4gICAgICAgICd7cnVuSWQ6JHJ1bklkLCBzdWJqZWN0U2hhOiRzdWJqZWN0U2hhfScpXG4gICAgaWYgW1sgJHJlcG9zaXRvcnkgIT0gXCIkUkVQT1NJVE9SWVwiIHx8ICR3b3JrZmxvdyAhPSBcIiRXT1JLRkxPV19OQU1FXCIgXV07IHRoZW5cbiAgICAgICAgZW1pdF9udWxsIFwicnVuOiRydW5faWRcIiBcIiRjYXNlX2pzb25cIiBcIiRldmlkZW5jZV9jdXRvZmZcIiBcIiRtYXR1cmVzX2F0XCIgXCJydW4gcmVwb3NpdG9yeSBvciB3b3JrZmxvdyBkb2VzIG5vdCBtYXRjaCB0aGUgZnJvemVuIGNvbnRyYWN0XCJcbiAgICAgICAgcmV0dXJuXG4gICAgZmlcbiAgICBpZiAhIHJlcG9ydHM9JChmaW5kX3JlcG9ydHMgXCIkcnVuX2lkXCIgXCIkcnVuX3NoYVwiIFwiJGV2aWRlbmNlX2N1dG9mZlwiKTsgdGhlblxuICAgICAgICBlbWl0X251bGwgXCJydW46JHJ1bl9pZFwiIFwiJGNhc2VfanNvblwiIFwiJGV2aWRlbmNlX2N1dG9mZlwiIFwiJG1hdHVyZXNfYXRcIiBcInJlcG9ydC1zZWFyY2gtdW5hdmFpbGFibGVcIlxuICAgICAgICByZXR1cm5cbiAgICBmaVxuICAgIHJlcG9ydF9udW1iZXJzPSQocHJpbnRmICclc1xcbicgXCIkcmVwb3J0c1wiIHwganEgLWMgJ1suW10ubnVtYmVyXScpXG4gICAgaWYgW1sgJHJlcG9ydF9udW1iZXJzID09ICdbXScgXV07IHRoZW5cbiAgICAgICAgZW1pdF9udWxsIFwicnVuOiRydW5faWRcIiBcIiRjYXNlX2pzb25cIiBcIiRldmlkZW5jZV9jdXRvZmZcIiBcIiRtYXR1cmVzX2F0XCIgXCJuby1xdWFsaWZ5aW5nLXJlcG9ydHNcIlxuICAgICAgICByZXR1cm5cbiAgICBmaVxuICAgIG1lcmdlZF9yZXBvcnRzPTBcbiAgICB3aGlsZSBJRlM9IHJlYWQgLXIgbnVtYmVyOyBkb1xuICAgICAgICBpZiByZXBvcnRfbWVyZ2VkX2J5X2N1dG9mZiBcIiRudW1iZXJcIiBcIiRldmlkZW5jZV9jdXRvZmZcIjsgdGhlbiBtZXJnZWRfcmVwb3J0cz0kKChtZXJnZWRfcmVwb3J0cyArIDEpKTsgZmlcbiAgICBkb25lIFx1MDAzYyBcdTAwM2MocHJpbnRmICclc1xcbicgXCIkcmVwb3J0X251bWJlcnNcIiB8IGpxIC1yICcuW10nKVxuICAgIGV2aWRlbmNlPSQoanEgLWNuIC0tYXJnanNvbiBxdWFsaWZ5aW5nUmVwb3J0cyBcIiQocHJpbnRmICclc1xcbicgXCIkcmVwb3J0X251bWJlcnNcIiB8IGpxIGxlbmd0aClcIiBcXFxuICAgICAgICAtLWFyZ2pzb24gbWVyZ2VkUmVwb3J0cyBcIiRtZXJnZWRfcmVwb3J0c1wiICd7dmFsaWQ6dHJ1ZSwgcXVhbGlmeWluZ1JlcG9ydHM6JHF1YWxpZnlpbmdSZXBvcnRzLCBtZXJnZWRSZXBvcnRzOiRtZXJnZWRSZXBvcnRzfScpXG4gICAgdmFsdWU9JChwcmludGYgJyVzXFxuJyBcIiRldmlkZW5jZVwiIHwgbWV0cmljKVxuICAgIHByb3ZlbmFuY2U9JChwcmludGYgJyVzXFxuJyBcIiRyZXBvcnRfbnVtYmVyc1wiIHwganEgLWMgLS1hcmcgcmVwb3NpdG9yeSBcIiRyZXBvc2l0b3J5XCIgXFxcbiAgICAgICAgJ1suW10gfCB7cmVwb3NpdG9yeTokcmVwb3NpdG9yeSwga2luZDpcImlzc3VlXCIsIHJlZjoodG9zdHJpbmcpfV0nKVxuICAgIGpxIC1jbiAtLWFyZ2pzb24gdmFsdWUgXCIkdmFsdWVcIiAtLWFyZyBvcHBvcnR1bml0eUtleSBcInJ1bjokcnVuX2lkXCIgLS1hcmdqc29uIGNhc2UgXCIkY2FzZV9qc29uXCIgXFxcbiAgICAgICAgLS1hcmcgZXZpZGVuY2VDdXRvZmYgXCIkZXZpZGVuY2VfY3V0b2ZmXCIgLS1hcmcgbWF0dXJlc0F0IFwiJG1hdHVyZXNfYXRcIiAtLWFyZ2pzb24gcHJvdmVuYW5jZSBcIiRwcm92ZW5hbmNlXCIgXFxcbiAgICAgICAgJ3t2YWx1ZTokdmFsdWUsIG9wcG9ydHVuaXR5S2V5OiRvcHBvcnR1bml0eUtleSwgY2FzZTokY2FzZSwgZXZpZGVuY2VDdXRvZmY6JGV2aWRlbmNlQ3V0b2ZmLFxuICAgICAgICAgIG1hdHVyZXNBdDokbWF0dXJlc0F0LCBwcm92ZW5hbmNlOiRwcm92ZW5hbmNlfSdcbn1cblxuY2FzZSAkezE6LX0gaW5cbiAgICAtLWRlZmluaXRpb24pIGRlZmluaXRpb24gOztcbiAgICAtLW1ldHJpYykgbWV0cmljIDs7XG4gICAgLS1ncmFkZS1ydW4pIGdyYWRlX3J1biA7O1xuICAgICopIHByaW50ZiAndXNhZ2U6ICVzIC0tZGVmaW5pdGlvbnwtLW1ldHJpY3wtLWdyYWRlLXJ1blxcbicgXCIkMFwiIFx1MDAzZVx1MDAyNjI7IGV4aXQgMSA7O1xuZXNhY1xuIn1d'); + env: + GH_TOKEN: ${{ github.token }} + GH_AW_RUN_CREATED_AT: ${{ needs.activation.outputs.run_created_at }} + - name: Redact grader outputs + if: always() + continue-on-error: true + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + with: + script: | + const path = require('path'); + const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); + const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); + setupGlobals(core, github, context, exec, io, getOctokit); + const { redactFilesInDir } = require(path.join(actionsDir, 'redact_secrets.cjs')); + await redactFilesInDir('/tmp/gh-aw/agent/graders'); + env: + GH_AW_SECRET_NAMES: 'GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN' + SECRET_GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }} + SECRET_GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }} + SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Write agent output placeholder if missing if: always() run: | @@ -1195,6 +1229,9 @@ jobs: path: | /tmp/gh-aw/agent_output.json /tmp/gh-aw/safeoutputs.jsonl + /tmp/gh-aw/agent/graders/grader_manifest.json + /tmp/gh-aw/agent/graders/grader_results.json + /tmp/gh-aw/agent/graders/operational_value_evaluator.sh if-no-files-found: ignore - name: Upload agent artifacts if: always() @@ -1214,6 +1251,9 @@ jobs: /tmp/gh-aw/github_rate_limits.jsonl /tmp/gh-aw/otel.jsonl /tmp/gh-aw/otlp-export-errors.jsonl + /tmp/gh-aw/agent/graders/grader_manifest.json + /tmp/gh-aw/agent/graders/grader_results.json + /tmp/gh-aw/agent/graders/operational_value_evaluator.sh /tmp/gh-aw/safeoutputs.jsonl /tmp/gh-aw/agent_output.json /tmp/gh-aw/aw-*.patch diff --git a/.github/workflows/duplicate-code-detector.md b/.github/workflows/duplicate-code-detector.md index f45f349a850..60235ae77e9 100644 --- a/.github/workflows/duplicate-code-detector.md +++ b/.github/workflows/duplicate-code-detector.md @@ -12,7 +12,9 @@ permissions: contents: read issues: read pull-requests: read - +graders: + operational-value: + run: .github/graders/duplicate-code-detector-operational-value.sh engine: codex model: copilot/auto