Skip to content

Commit 987b3a8

Browse files
authored
Merge branch 'main' into campaign-orchestrator
2 parents e6dd2d1 + d38ede8 commit 987b3a8

File tree

8 files changed

+150
-25
lines changed

8 files changed

+150
-25
lines changed

.github/aw/github-agentic-workflows.md

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.github/workflows/install.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,8 @@ jobs:
108108

109109
- name: Test full install script (without dummy version)
110110
shell: bash
111+
env:
112+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
111113
run: |
112114
chmod +x install-gh-aw.sh
113115

docs/src/content/docs/reference/frontmatter-full.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,11 @@ on:
216216
names: []
217217
# Array items: Label name
218218

219+
# Whether to lock the issue for the agent when the workflow runs (prevents
220+
# concurrent modifications)
221+
# (optional)
222+
lock-for-agent: true
223+
219224
# Issue comment event trigger
220225
# (optional)
221226
issue_comment:

docs/src/content/docs/reference/glossary.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,22 @@ The process of checking workflow files for errors, security issues, and best pra
195195
### Cache Memory
196196
Persistent storage for workflows that preserves data between runs. Configured using `cache-memory:` in the tools section, it enables workflows to remember information and build on previous interactions.
197197

198+
### Campaign
199+
A finite, enterprise-scale initiative with explicit ownership, approval gates, and executive visibility. Campaigns orchestrate business outcomes (security remediation, dependency updates, compliance enforcement) across multiple repositories with governance, accountability, and ROI tracking. Unlike regular workflows that execute operations, campaigns manage business initiatives with measurable outcomes, defined budgets, stakeholder reporting, and compliance audit trails.
200+
201+
**Key characteristics:**
202+
- Finite duration (days to months) with clear start and end
203+
- Named owners and executive sponsors
204+
- Formal approval gates and change control
205+
- Budget tracking with ROI metrics
206+
- Stateful execution using repo-memory for audit trails
207+
- Cross-team and cross-repository coordination
208+
- Executive dashboards and KPI reporting
209+
210+
**File naming:** Use `.campaign.md` extension (e.g., `security-compliance.campaign.md`)
211+
212+
See the [Campaigns Guide](/gh-aw/guides/campaigns/) for implementation patterns and examples.
213+
198214
### Command Triggers
199215
Special triggers that respond to slash commands in issue and PR comments (e.g., `/review`, `/deploy`). Configured using the `command:` section with a command name.
200216

install-gh-aw.sh

Lines changed: 50 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
# Script to download and install gh-aw binary for the current OS and architecture
44
# Supports: Linux, macOS (Darwin), FreeBSD, Windows (Git Bash/MSYS/Cygwin)
55
# Usage: ./install-gh-aw.sh [version]
6-
# If no version is specified, it will use the latest release
6+
# If no version is specified, it will fetch and use the latest release
7+
# Example: ./install-gh-aw.sh v1.0.0
78

89
set -e # Exit on any error
910

@@ -109,48 +110,72 @@ print_info "Detected OS: $OS -> $OS_NAME"
109110
print_info "Detected architecture: $ARCH -> $ARCH_NAME"
110111
print_info "Platform: $PLATFORM"
111112

112-
# Function to fetch release data with fallback for invalid token
113+
# Function to fetch release data with fallback for invalid token and retry logic
113114
fetch_release_data() {
114115
local url=$1
116+
local max_retries=3
117+
local retry_delay=2
115118
local use_auth=false
116-
local curl_args=("-s" "-f")
117119

118120
# Try with authentication if GH_TOKEN is set
119121
if [ -n "$GH_TOKEN" ]; then
120122
use_auth=true
121-
curl_args+=("-H" "Authorization: Bearer $GH_TOKEN")
122123
fi
123124

124-
# Make the API call
125-
local response
126-
response=$(curl "${curl_args[@]}" "$url" 2>/dev/null)
127-
local exit_code=$?
128-
129-
# If the call failed and we used authentication, try again without it
130-
if [ $exit_code -ne 0 ] && [ "$use_auth" = true ]; then
131-
print_warning "API call with GH_TOKEN failed. Retrying without authentication..."
132-
print_warning "Your GH_TOKEN may be incompatible (typically SSO) with this request."
133-
response=$(curl -s -f "$url" 2>/dev/null)
134-
exit_code=$?
135-
fi
136-
137-
if [ $exit_code -ne 0 ]; then
138-
return 1
139-
fi
125+
# Retry loop
126+
for attempt in $(seq 1 $max_retries); do
127+
local curl_args=("-s" "-f")
128+
129+
# Add auth header if using authentication
130+
if [ "$use_auth" = true ]; then
131+
curl_args+=("-H" "Authorization: Bearer $GH_TOKEN")
132+
fi
133+
134+
print_info "Fetching release data (attempt $attempt/$max_retries)..." >&2
135+
136+
# Make the API call
137+
local response
138+
response=$(curl "${curl_args[@]}" "$url" 2>/dev/null)
139+
local exit_code=$?
140+
141+
# Success
142+
if [ $exit_code -eq 0 ] && [ -n "$response" ]; then
143+
echo "$response"
144+
return 0
145+
fi
146+
147+
# If this was the first attempt with auth and it failed, try without auth
148+
if [ "$attempt" -eq 1 ] && [ "$use_auth" = true ]; then
149+
print_warning "API call with GH_TOKEN failed. Retrying without authentication..." >&2
150+
print_warning "Your GH_TOKEN may be incompatible (typically SSO) with this request." >&2
151+
use_auth=false
152+
# Don't count this as a retry attempt, just switch auth mode
153+
continue
154+
fi
155+
156+
# If we haven't exhausted retries, wait and try again
157+
if [ "$attempt" -lt "$max_retries" ]; then
158+
print_warning "Fetch attempt $attempt failed (exit code: $exit_code). Retrying in ${retry_delay}s..." >&2
159+
sleep $retry_delay
160+
retry_delay=$((retry_delay * 2))
161+
else
162+
print_error "Failed to fetch release data after $max_retries attempts" >&2
163+
fi
164+
done
140165

141-
echo "$response"
142-
return 0
166+
return 1
143167
}
144168

145169
# Get version (use provided version or fetch latest)
146170
VERSION=${1:-""}
147171
REPO="githubnext/gh-aw"
148172

149173
if [ -z "$VERSION" ]; then
150-
print_info "Fetching latest release information..."
174+
print_info "No version specified, fetching latest release information from GitHub..."
151175

152176
if ! LATEST_RELEASE=$(fetch_release_data "https://api.github.com/repos/$REPO/releases/latest"); then
153-
print_error "Failed to fetch latest release information"
177+
print_error "Failed to fetch latest release information from GitHub API"
178+
print_info "You can specify a version directly: ./install-gh-aw.sh v1.0.0"
154179
exit 1
155180
fi
156181

@@ -206,7 +231,7 @@ for attempt in $(seq 1 $MAX_RETRIES); do
206231
print_success "Binary downloaded successfully"
207232
break
208233
else
209-
if [ $attempt -eq $MAX_RETRIES ]; then
234+
if [ "$attempt" -eq "$MAX_RETRIES" ]; then
210235
print_error "Failed to download binary from $DOWNLOAD_URL after $MAX_RETRIES attempts"
211236
print_info "Please check if the version and platform combination exists in the releases."
212237
exit 1

pkg/workflow/js.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,9 @@ var sanitizeWorkflowNameScript string
112112
//go:embed js/load_agent_output.cjs
113113
var loadAgentOutputScript string
114114

115+
//go:embed js/lock-issue.cjs
116+
var lockIssueScript string
117+
115118
//go:embed js/staged_preview.cjs
116119
var stagedPreviewScript string
117120

@@ -278,6 +281,7 @@ func GetJavaScriptSources() map[string]string {
278281
"sanitize_label_content.cjs": sanitizeLabelContentScript,
279282
"sanitize_workflow_name.cjs": sanitizeWorkflowNameScript,
280283
"load_agent_output.cjs": loadAgentOutputScript,
284+
"lock-issue.cjs": lockIssueScript,
281285
"staged_preview.cjs": stagedPreviewScript,
282286
"assign_agent_helpers.cjs": assignAgentHelpersScript,
283287
"safe_output_helpers.cjs": safeOutputHelpersScript,

pkg/workflow/js/lock-issue.cjs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// @ts-check
2+
/// <reference types="@actions/github-script" />
3+
4+
/**
5+
* Lock a GitHub issue without providing a reason
6+
* This script is used in the activation job when lock-for-agent is enabled
7+
* to prevent concurrent modifications during agent workflow execution
8+
*/
9+
10+
async function main() {
11+
// Get issue number from context
12+
const issueNumber = context.issue.number;
13+
14+
if (!issueNumber) {
15+
core.setFailed("Issue number not found in context");
16+
return;
17+
}
18+
19+
const owner = context.repo.owner;
20+
const repo = context.repo.repo;
21+
22+
core.info(`Locking issue #${issueNumber} for agent workflow execution`);
23+
24+
try {
25+
// Lock the issue without providing a lock_reason parameter
26+
await github.rest.issues.lock({
27+
owner,
28+
repo,
29+
issue_number: issueNumber,
30+
});
31+
32+
core.info(`✅ Successfully locked issue #${issueNumber}`);
33+
} catch (error) {
34+
const errorMessage = error instanceof Error ? error.message : String(error);
35+
core.error(`Failed to lock issue: ${errorMessage}`);
36+
core.setFailed(`Failed to lock issue #${issueNumber}: ${errorMessage}`);
37+
}
38+
}
39+
40+
await main();

scripts/test-install-script.sh

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,38 @@ else
232232
exit 1
233233
fi
234234

235+
# Verify the function has retry logic with max_retries
236+
if grep -q 'local max_retries=3' "$PROJECT_ROOT/install-gh-aw.sh"; then
237+
echo " ✓ PASS: Function has max_retries=3 variable"
238+
else
239+
echo " ✗ FAIL: Function does not have max_retries variable"
240+
exit 1
241+
fi
242+
243+
# Verify the function has retry loop
244+
if grep -q 'for attempt in $(seq 1 $max_retries)' "$PROJECT_ROOT/install-gh-aw.sh"; then
245+
echo " ✓ PASS: Function has retry loop"
246+
else
247+
echo " ✗ FAIL: Function does not have retry loop"
248+
exit 1
249+
fi
250+
251+
# Verify the function logs fetch attempts
252+
if grep -q 'Fetching release data (attempt' "$PROJECT_ROOT/install-gh-aw.sh"; then
253+
echo " ✓ PASS: Function logs fetch attempts"
254+
else
255+
echo " ✗ FAIL: Function does not log fetch attempts"
256+
exit 1
257+
fi
258+
259+
# Verify the function has exponential backoff for retries
260+
if grep -q 'retry_delay=\$((retry_delay \* 2))' "$PROJECT_ROOT/install-gh-aw.sh"; then
261+
echo " ✓ PASS: Function has exponential backoff for retries"
262+
else
263+
echo " ✗ FAIL: Function does not have exponential backoff"
264+
exit 1
265+
fi
266+
235267
# Test 9: Verify retry logic for downloads
236268
echo ""
237269
echo "Test 9: Verify download retry logic"

0 commit comments

Comments
 (0)