Visitar URL original
chore: sync actions from gh-aw@v1.2.0 (#296) · github/gh-aw-actions@4607d46 · GitHub
Skip to content

Commit 4607d46

Browse files
chore: sync actions from gh-aw@v1.2.0 (#296)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
1 parent 221ba4f commit 4607d46

177 files changed

Lines changed: 9756 additions & 2296 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎setup-cli/install.sh‎

Lines changed: 83 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -97,19 +97,19 @@ fi
9797

9898
# Check if sha256sum or shasum is available (for checksum verification)
9999
HAS_CHECKSUM_TOOL=false
100-
CHECKSUM_CMD=""
100+
CHECKSUM_CMD=()
101101
if command -v sha256sum &> /dev/null; then
102102
HAS_CHECKSUM_TOOL=true
103-
CHECKSUM_CMD="sha256sum"
103+
CHECKSUM_CMD=(sha256sum)
104104
elif command -v shasum &> /dev/null; then
105105
HAS_CHECKSUM_TOOL=true
106-
CHECKSUM_CMD="shasum -a 256"
106+
CHECKSUM_CMD=(shasum -a 256)
107107
fi
108108

109109
if [ "$SKIP_CHECKSUM" = false ] && [ "$HAS_CHECKSUM_TOOL" = false ]; then
110-
print_warning "Neither sha256sum nor shasum is available. Checksum verification will be skipped."
111-
print_warning "To suppress this warning, use --skip-checksum flag."
112-
SKIP_CHECKSUM=true
110+
print_error "Neither sha256sum nor shasum is available. Cannot verify binary integrity."
111+
print_info "Install sha256sum or shasum and retry."
112+
exit 1
113113
fi
114114

115115
# Determine OS and architecture
@@ -527,63 +527,90 @@ fi
527527
# Download and verify checksums if not skipped
528528
if [ "$SKIP_CHECKSUM" = false ]; then
529529
print_info "Downloading checksums file..."
530-
CHECKSUMS_DOWNLOADED=false
531-
532-
for attempt in $(seq 1 $MAX_RETRIES); do
533-
if curl -L -f --connect-timeout 15 --max-time 60 -o "$CHECKSUMS_PATH" "$CHECKSUMS_URL" 2>/dev/null; then
534-
CHECKSUMS_DOWNLOADED=true
535-
print_success "Checksums file downloaded successfully"
536-
break
537-
else
538-
if [ "$attempt" -eq "$MAX_RETRIES" ]; then
539-
print_warning "Failed to download checksums file after $MAX_RETRIES attempts"
540-
print_warning "Checksum verification will be skipped for this version."
541-
print_info "This may occur for older releases that don't include checksums."
542-
break
543-
else
530+
download_checksums_with_retry() {
531+
local url="$1"
532+
for attempt in $(seq 1 $MAX_RETRIES); do
533+
if curl -L -f --connect-timeout 15 --max-time 60 -o "$CHECKSUMS_PATH" "$url" 2>/dev/null; then
534+
print_success "Checksums file downloaded successfully"
535+
return 0
536+
fi
537+
if [ "$attempt" -lt "$MAX_RETRIES" ]; then
544538
print_warning "Checksum download attempt $attempt failed. Retrying in 2s..."
545539
sleep 2
546540
fi
547-
fi
548-
done
549-
550-
# Verify checksum if we downloaded it successfully
551-
if [ "$CHECKSUMS_DOWNLOADED" = true ]; then
552-
print_info "Verifying binary checksum..."
553-
554-
# Determine the expected filename in the checksums file
555-
EXPECTED_FILENAME="$PLATFORM"
556-
if [ "$OS_NAME" = "windows" ]; then
557-
EXPECTED_FILENAME="${PLATFORM}.exe"
558-
fi
559-
560-
# Extract the expected checksum from the checksums file (exact filename match on field 2)
561-
EXPECTED_CHECKSUM=$(awk -v f="$EXPECTED_FILENAME" '$2 == f {print $1}' "$CHECKSUMS_PATH")
562-
563-
if [ -z "$EXPECTED_CHECKSUM" ]; then
564-
print_warning "Checksum for $EXPECTED_FILENAME not found in checksums file"
565-
print_warning "Checksum verification will be skipped."
566-
else
567-
# Compute the actual checksum of the downloaded binary
568-
ACTUAL_CHECKSUM=$($CHECKSUM_CMD "$STAGED_BINARY_PATH" | awk '{print $1}')
569-
570-
if [ "$ACTUAL_CHECKSUM" = "$EXPECTED_CHECKSUM" ]; then
571-
print_success "Checksum verification passed!"
572-
print_info "Expected: $EXPECTED_CHECKSUM"
573-
print_info "Actual: $ACTUAL_CHECKSUM"
541+
done
542+
return 1
543+
}
544+
545+
if ! download_checksums_with_retry "$CHECKSUMS_URL"; then
546+
if [ -n "$FALLBACK_CHECKSUMS_URL" ] && [ "$CHECKSUMS_URL" != "$FALLBACK_CHECKSUMS_URL" ]; then
547+
print_warning "Failed to download checksums from latest redirect URL. Retrying with resolved tag URL ($LATEST_TAG)."
548+
CHECKSUMS_URL="$FALLBACK_CHECKSUMS_URL"
549+
if download_checksums_with_retry "$CHECKSUMS_URL"; then
550+
CHECKSUMS_DOWNLOADED=true
574551
else
575-
print_error "Checksum verification failed!"
576-
print_error "Expected: $EXPECTED_CHECKSUM"
577-
print_error "Actual: $ACTUAL_CHECKSUM"
578-
print_error "The downloaded binary may be corrupted or tampered with."
579-
print_info "To skip checksum verification, use: ./install-gh-aw.sh $VERSION --skip-checksum"
580-
exit 1
552+
CHECKSUMS_DOWNLOADED=false
581553
fi
554+
else
555+
CHECKSUMS_DOWNLOADED=false
556+
fi
557+
if [ "$CHECKSUMS_DOWNLOADED" = false ]; then
558+
print_error "Failed to download checksums file after $MAX_RETRIES attempts"
559+
print_error "Cannot verify binary integrity. Installation aborted."
560+
print_info "Check that $CHECKSUMS_URL is available and retry."
561+
print_info "For older releases without checksums.txt, prefer a release with checksums or explicitly use --skip-checksum (disables integrity verification; corrupted or tampered binaries may be executed)."
562+
exit 1
582563
fi
583-
584-
# Clean up checksums file
585-
rm -f "$CHECKSUMS_PATH"
586564
fi
565+
566+
print_info "Verifying binary checksum..."
567+
568+
EXPECTED_FILENAME="$PLATFORM"
569+
if [ "$OS_NAME" = "windows" ]; then
570+
EXPECTED_FILENAME="${PLATFORM}.exe"
571+
fi
572+
573+
# Require exactly one well-formed SHA256 entry for the exact release asset.
574+
if ! EXPECTED_CHECKSUM=$(awk -v f="$EXPECTED_FILENAME" '
575+
$2 == f {
576+
count++
577+
if (NF != 2 || length($1) != 64 || $1 !~ /^[0-9a-fA-F]+$/) {
578+
invalid = 1
579+
}
580+
checksum = tolower($1)
581+
}
582+
END {
583+
if (count != 1 || invalid) {
584+
exit 1
585+
}
586+
print checksum
587+
}
588+
' "$CHECKSUMS_PATH"); then
589+
print_error "Missing or malformed checksum for $EXPECTED_FILENAME in checksums file."
590+
print_error "Cannot verify binary integrity. Installation aborted."
591+
print_info "Use a release with exactly one valid SHA256 checksum for $EXPECTED_FILENAME."
592+
exit 1
593+
fi
594+
595+
if ! CHECKSUM_OUTPUT=$("${CHECKSUM_CMD[@]}" "$STAGED_BINARY_PATH"); then
596+
print_error "Failed to compute binary checksum. Installation aborted."
597+
exit 1
598+
fi
599+
ACTUAL_CHECKSUM=$(printf '%s\n' "$CHECKSUM_OUTPUT" | awk '{print tolower($1)}')
600+
601+
if [[ "$ACTUAL_CHECKSUM" =~ ^[0-9a-f]{64}$ ]] && [ "$ACTUAL_CHECKSUM" = "$EXPECTED_CHECKSUM" ]; then
602+
print_success "Checksum verification passed!"
603+
print_info "Expected: $EXPECTED_CHECKSUM"
604+
print_info "Actual: $ACTUAL_CHECKSUM"
605+
else
606+
print_error "Checksum verification failed!"
607+
print_error "Expected: $EXPECTED_CHECKSUM"
608+
print_error "Actual: $ACTUAL_CHECKSUM"
609+
print_error "The downloaded binary may be corrupted or tampered with. Installation aborted."
610+
exit 1
611+
fi
612+
613+
rm -f "$CHECKSUMS_PATH"
587614
else
588615
print_warning "Checksum verification skipped (--skip-checksum flag used)"
589616
fi

‎setup/js/action_conclusion_otlp.cjs‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,10 @@ async function run() {
8585

8686
await sendOtlpSpan.sendJobConclusionSpan(spanName, { startMs });
8787

88+
if (getActionInput("JOB_NAME") === "conclusion") {
89+
await require("./send_otlp_session_logs.cjs").sendSessionLogs();
90+
}
91+
8892
if (endpoints) {
8993
core.info("[otlp] conclusion span export attempted");
9094
}

‎setup/js/add_comment.cjs‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ const { createAuthenticatedGitHubClient } = require("./handler_auth.cjs");
1919
const { getMessages } = require("./messages_core.cjs");
2020
const { sanitizeContent } = require("./sanitize_content.cjs");
2121
const { MAX_COMMENT_LENGTH, MAX_COMMENT_INPUT_LENGTH, MAX_MENTIONS, MAX_LINKS, enforceCommentLimits } = require("./comment_limit_helpers.cjs");
22-
const { renderSizedReport } = require("./report_size.cjs");
22+
const { fitMarkdownBody } = require("./report_size.cjs");
2323
const { createDiscussionComment, resolveTopLevelDiscussionCommentId } = require("./github_api_helpers.cjs");
2424
const { logStagedPreviewInfo } = require("./staged_preview.cjs");
2525
const { ERR_CONFIG, ERR_NOT_FOUND } = require("./error_codes.cjs");
@@ -839,7 +839,7 @@ async function main(config = {}) {
839839
// Enforce max limits again after adding footer and metadata
840840
// Length and mention limits include metadata; only agent content counts toward the link limit.
841841
try {
842-
processedBody = renderSizedReport(report, MAX_COMMENT_LENGTH).body;
842+
processedBody = fitMarkdownBody(report, { maxLength: MAX_COMMENT_LENGTH });
843843
enforceCommentLimits(processedBody, { maxLinks, linkBody });
844844
} catch (error) {
845845
const errorMessage = getErrorMessage(error);

‎setup/js/add_labels_schema.cjs‎

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
"use strict";
2+
3+
const ADD_LABELS_OBJECT_ONLY_FIELD_DESC = "Labels to add. Each label must be an object matching the configured item schema; plain string label names are not permitted. Labels must exist in the repository.";
4+
5+
const OBJECT_SCHEMA_KEYS = new Set(["type", "properties", "required", "additionalProperties", "title", "description", "$comment"]);
6+
const PROPERTY_SCHEMA_KEYS = new Set(["type", "enum", "const", "title", "description", "$comment", "default", "examples"]);
7+
8+
/**
9+
* @param {unknown} value
10+
* @returns {value is Record<string, unknown>}
11+
*/
12+
function isRecord(value) {
13+
return value !== null && typeof value === "object" && !Array.isArray(value);
14+
}
15+
16+
/**
17+
* @param {unknown} value
18+
* @param {unknown} type
19+
* @returns {boolean}
20+
*/
21+
function matchesType(value, type) {
22+
if (type === undefined) return true;
23+
const types = Array.isArray(type) ? type : [type];
24+
return types.some(candidate => {
25+
switch (candidate) {
26+
case "array":
27+
return Array.isArray(value);
28+
case "object":
29+
return isRecord(value);
30+
case "integer":
31+
return typeof value === "number" && Number.isInteger(value);
32+
case "number":
33+
return typeof value === "number" && Number.isFinite(value);
34+
case "null":
35+
return value === null;
36+
case "boolean":
37+
return typeof value === "boolean";
38+
case "string":
39+
return typeof value === "string";
40+
default:
41+
return false;
42+
}
43+
});
44+
}
45+
46+
/**
47+
* @param {string} fieldName
48+
* @param {unknown} schema
49+
* @returns {{value: unknown} | null}
50+
*/
51+
function buildAddLabelsExampleValue(fieldName, schema) {
52+
if (!isRecord(schema) || Object.keys(schema).some(key => !PROPERTY_SCHEMA_KEYS.has(key))) {
53+
return null;
54+
}
55+
56+
const type = schema.type;
57+
let value;
58+
if (Object.prototype.hasOwnProperty.call(schema, "const")) {
59+
value = schema.const;
60+
if (Array.isArray(schema.enum) && !schema.enum.some(candidate => Object.is(candidate, value))) {
61+
return null;
62+
}
63+
} else if (Array.isArray(schema.enum)) {
64+
value = schema.enum.find(candidate => matchesType(candidate, type));
65+
if (value === undefined) return null;
66+
} else {
67+
switch (type) {
68+
case "boolean":
69+
value = true;
70+
break;
71+
case "integer":
72+
case "number":
73+
value = 0;
74+
break;
75+
case "string":
76+
value = fieldName === "name" ? "bug" : fieldName === "rationale" ? "Why this label applies" : "value";
77+
break;
78+
default:
79+
return null;
80+
}
81+
}
82+
83+
return matchesType(value, type) ? { value } : null;
84+
}
85+
86+
/**
87+
* @param {unknown} itemSchema
88+
* @returns {Record<string, unknown> | null}
89+
*/
90+
function getAddLabelsObjectSchema(itemSchema) {
91+
if (!isRecord(itemSchema)) return null;
92+
if (itemSchema.type === "object") return itemSchema;
93+
if (Array.isArray(itemSchema.oneOf)) {
94+
return itemSchema.oneOf.find(schema => isRecord(schema) && schema.type === "object") ?? null;
95+
}
96+
return null;
97+
}
98+
99+
/**
100+
* Build the schema-derived required fields and an example only when all required values
101+
* can be inferred without violating declared schema constraints.
102+
* @param {unknown} itemSchema
103+
* @returns {{required: string[], fields: string[], example: string | null} | null}
104+
*/
105+
function buildAddLabelsObjectShape(itemSchema) {
106+
const objectSchema = getAddLabelsObjectSchema(itemSchema);
107+
if (!objectSchema) return null;
108+
109+
const schemaKeys = Object.keys(objectSchema);
110+
const safeObjectSchema = schemaKeys.every(key => OBJECT_SCHEMA_KEYS.has(key));
111+
const properties = isRecord(objectSchema.properties) ? objectSchema.properties : {};
112+
const required = Array.isArray(objectSchema.required) ? objectSchema.required.filter(field => typeof field === "string") : [];
113+
/** @type {string | null} */
114+
let example = null;
115+
116+
if (safeObjectSchema && required.length > 0) {
117+
const exampleValue = Object.create(null);
118+
let canBuildExample = true;
119+
for (const field of required) {
120+
if (!Object.prototype.hasOwnProperty.call(properties, field)) {
121+
canBuildExample = false;
122+
break;
123+
}
124+
const fieldValue = buildAddLabelsExampleValue(field, properties[field]);
125+
if (!fieldValue) {
126+
canBuildExample = false;
127+
break;
128+
}
129+
exampleValue[field] = fieldValue.value;
130+
}
131+
if (canBuildExample) example = JSON.stringify(exampleValue);
132+
}
133+
134+
return {
135+
required,
136+
fields: Object.keys(properties),
137+
example,
138+
};
139+
}
140+
141+
/**
142+
* Build accurate object-only guidance for the generated add_labels labels field.
143+
* @param {unknown} itemSchema
144+
* @returns {string}
145+
*/
146+
function buildAddLabelsObjectOnlyFieldDesc(itemSchema) {
147+
const shape = buildAddLabelsObjectShape(itemSchema);
148+
if (!shape) return ADD_LABELS_OBJECT_ONLY_FIELD_DESC;
149+
150+
const guidance = [];
151+
if (shape.required.length > 0) {
152+
guidance.push(`Required fields: ${shape.required.join(", ")}.`);
153+
} else {
154+
guidance.push("No required fields are declared by the configured item schema.");
155+
if (shape.fields.length > 0) {
156+
guidance.push(`Available fields: ${shape.fields.join(", ")}.`);
157+
}
158+
}
159+
if (shape.example) {
160+
guidance.push(`Example: [${shape.example}].`);
161+
}
162+
163+
return `Labels to add. Each label must be an object matching the configured item schema; plain string label names are not permitted. ${guidance.join(" ")} Labels must exist in the repository.`;
164+
}
165+
166+
module.exports = {
167+
buildAddLabelsObjectOnlyFieldDesc,
168+
buildAddLabelsObjectShape,
169+
};

0 commit comments

Comments
 (0)