Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 4 additions & 0 deletions .changeset/patch-repo-memory-ghe.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions .github/workflows/agent-performance-analyzer.lock.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions .github/workflows/audit-workflows.lock.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 10 additions & 3 deletions actions/setup/js/push_repo_memory.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ async function main() {

const ghToken = process.env.GH_TOKEN;
const githubRunId = process.env.GITHUB_RUN_ID || "unknown";
const githubServerUrl = process.env.GITHUB_SERVER_URL || "https://github.com";

// Log environment variable configuration for debugging
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice GitHub Enterprise support! Dynamically extracting the server host ensures compatibility with both github.com and GHE instances. 👍

core.info("Environment configuration:");
Expand Down Expand Up @@ -131,7 +132,9 @@ async function main() {
// Checkout or create the memory branch
core.info(`Checking out branch: ${branchName}...`);
try {
const repoUrl = `https://x-access-token:${ghToken}@github.com/${targetRepo}.git`;
// Extract host from server URL (remove https:// prefix)
const serverHost = githubServerUrl.replace(/^https?:\/\//, "");
Copy link

Copilot AI Feb 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The serverHost extraction logic is duplicated three times in this file (lines 136, 349, and 361). Consider extracting this to a helper function at the top of the file to avoid code duplication and improve maintainability.

Copilot uses AI. Check for mistakes.
const repoUrl = `https://x-access-token:${ghToken}@${serverHost}/${targetRepo}.git`;

// Try to fetch the branch
try {
Expand Down Expand Up @@ -342,7 +345,9 @@ async function main() {
// Pull with merge strategy (ours wins on conflicts)
core.info(`Pulling latest changes from ${branchName}...`);
try {
const repoUrl = `https://x-access-token:${ghToken}@github.com/${targetRepo}.git`;
// Extract host from server URL (remove https:// prefix)
const serverHost = githubServerUrl.replace(/^https?:\/\//, "");
const repoUrl = `https://x-access-token:${ghToken}@${serverHost}/${targetRepo}.git`;
execGitSync(["pull", "--no-rebase", "-X", "ours", repoUrl, branchName], { stdio: "inherit" });
} catch (error) {
// Pull might fail if branch doesn't exist yet or on conflicts - this is acceptable
Expand All @@ -352,7 +357,9 @@ async function main() {
// Push changes
core.info(`Pushing changes to ${branchName}...`);
try {
const repoUrl = `https://x-access-token:${ghToken}@github.com/${targetRepo}.git`;
// Extract host from server URL (remove https:// prefix)
const serverHost = githubServerUrl.replace(/^https?:\/\//, "");
const repoUrl = `https://x-access-token:${ghToken}@${serverHost}/${targetRepo}.git`;
execGitSync(["push", repoUrl, `HEAD:${branchName}`], { stdio: "inherit" });
core.info(`Successfully pushed changes to ${branchName} branch`);
} catch (error) {
Expand Down
14 changes: 12 additions & 2 deletions actions/setup/sh/clone_repo_memory_branch.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
# TARGET_REPO: Repository to clone from (e.g., owner/repo)
# MEMORY_DIR: Directory to clone into
# CREATE_ORPHAN: Whether to create orphan branch if it doesn't exist (true/false)
# GITHUB_SERVER_URL: GitHub server URL (e.g., https://github.com or https://ghe.company.com)

set -e

Expand Down Expand Up @@ -37,9 +38,18 @@ if [ -z "$CREATE_ORPHAN" ]; then
exit 1
fi

# Default to github.com if not set
if [ -z "$GITHUB_SERVER_URL" ]; then
GITHUB_SERVER_URL="https://github.com"
fi

# Extract host from server URL (remove https:// or http:// prefix)
SERVER_HOST="${GITHUB_SERVER_URL#https://}"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good defensive programming - handling both https:// and `(redacted) prefix removal. The double substitution ensures compatibility with different server URL formats.

SERVER_HOST="${SERVER_HOST#http://}"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot echo value


# Try to clone the branch (don't fail if it doesn't exist)
set +e
git clone --depth 1 --single-branch --branch "$BRANCH_NAME" "https://x-access-token:${GH_TOKEN}@github.com/${TARGET_REPO}.git" "$MEMORY_DIR" 2>/dev/null
git clone --depth 1 --single-branch --branch "$BRANCH_NAME" "https://x-access-token:${GH_TOKEN}@${SERVER_HOST}/${TARGET_REPO}.git" "$MEMORY_DIR" 2>/dev/null
CLONE_EXIT_CODE=$?
set -e

Expand All @@ -53,7 +63,7 @@ if [ $CLONE_EXIT_CODE -ne 0 ]; then
git checkout --orphan "$BRANCH_NAME"
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git remote add origin "https://x-access-token:${GH_TOKEN}@github.com/${TARGET_REPO}.git"
git remote add origin "https://x-access-token:${GH_TOKEN}@${SERVER_HOST}/${TARGET_REPO}.git"
else
echo "Branch $BRANCH_NAME does not exist and create-orphan is false, skipping"
mkdir -p "$MEMORY_DIR"
Expand Down
10 changes: 8 additions & 2 deletions pkg/workflow/repo_memory.go
Original file line number Diff line number Diff line change
Expand Up @@ -481,10 +481,14 @@ func generateRepoMemoryPushSteps(builder *strings.Builder, data *WorkflowData) {
builder.WriteString(" if: always()\n")
builder.WriteString(" env:\n")
builder.WriteString(" GH_TOKEN: ${{ github.token }}\n")
builder.WriteString(" GITHUB_SERVER_URL: ${{ github.server_url }}\n")
builder.WriteString(" run: |\n")
builder.WriteString(" set -e\n")
fmt.Fprintf(builder, " cd \"%s\" || exit 0\n", memoryDir)
builder.WriteString(" \n")
builder.WriteString(" # Extract host from server URL (remove https:// prefix)\n")
builder.WriteString(" SERVER_HOST=\"${GITHUB_SERVER_URL#https://}\"\n")
builder.WriteString(" \n")
builder.WriteString(" # Check if we have any changes to commit\n")
builder.WriteString(" if [ -n \"$(git status --porcelain)\" ]; then\n")
builder.WriteString(" echo \"Changes detected in repo memory, committing and pushing...\"\n")
Expand Down Expand Up @@ -523,13 +527,13 @@ func generateRepoMemoryPushSteps(builder *strings.Builder, data *WorkflowData) {
builder.WriteString(" \n")
builder.WriteString(" # Pull with ours merge strategy (our changes win in conflicts)\n")
builder.WriteString(" set +e\n")
fmt.Fprintf(builder, " git pull --no-rebase -s recursive -X ours \"https://x-access-token:${GH_TOKEN}@github.com/%s.git\" \"%s\" 2>&1\n",
fmt.Fprintf(builder, " git pull --no-rebase -s recursive -X ours \"https://x-access-token:${GH_TOKEN}@${SERVER_HOST}/%s.git\" \"%s\" 2>&1\n",
targetRepo, memory.BranchName)
builder.WriteString(" PULL_EXIT_CODE=$?\n")
builder.WriteString(" set -e\n")
builder.WriteString(" \n")
builder.WriteString(" # Push changes (force push if needed due to conflict resolution)\n")
fmt.Fprintf(builder, " git push \"https://x-access-token:${GH_TOKEN}@github.com/%s.git\" \"HEAD:%s\"\n",
fmt.Fprintf(builder, " git push \"https://x-access-token:${GH_TOKEN}@${SERVER_HOST}/%s.git\" \"HEAD:%s\"\n",
targetRepo, memory.BranchName)
builder.WriteString(" \n")
builder.WriteString(" echo \"Successfully pushed changes to repo memory\"\n")
Expand Down Expand Up @@ -563,6 +567,7 @@ func generateRepoMemorySteps(builder *strings.Builder, data *WorkflowData) {
fmt.Fprintf(builder, " - name: Clone repo-memory branch (%s)\n", memory.ID)
builder.WriteString(" env:\n")
builder.WriteString(" GH_TOKEN: ${{ github.token }}\n")
builder.WriteString(" GITHUB_SERVER_URL: ${{ github.server_url }}\n")
fmt.Fprintf(builder, " BRANCH_NAME: %s\n", memory.BranchName)
fmt.Fprintf(builder, " TARGET_REPO: %s\n", targetRepo)
fmt.Fprintf(builder, " MEMORY_DIR: %s\n", memoryDir)
Expand Down Expand Up @@ -650,6 +655,7 @@ func (c *Compiler) buildPushRepoMemoryJob(data *WorkflowData, threatDetectionEna
step.WriteString(" env:\n")
step.WriteString(" GH_TOKEN: ${{ github.token }}\n")
step.WriteString(" GITHUB_RUN_ID: ${{ github.run_id }}\n")
step.WriteString(" GITHUB_SERVER_URL: ${{ github.server_url }}\n")
fmt.Fprintf(&step, " ARTIFACT_DIR: %s\n", artifactDir)
fmt.Fprintf(&step, " MEMORY_ID: %s\n", memory.ID)
fmt.Fprintf(&step, " TARGET_REPO: %s\n", targetRepo)
Expand Down
58 changes: 58 additions & 0 deletions pkg/workflow/repo_memory_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -306,3 +306,61 @@ This workflow has repo-memory disabled.
t.Error("Should not have repo memory prompt when disabled")
}
}

// TestRepoMemoryGitHubEnterpriseSupport tests that GITHUB_SERVER_URL is used for GHE compatibility
func TestRepoMemoryGitHubEnterpriseSupport(t *testing.T) {
tmpDir := testutil.TempDir(t, "test-*")
workflowPath := filepath.Join(tmpDir, "test-workflow.md")

content := `---
name: Test Repo Memory GHE
on: workflow_dispatch
engine: copilot
tools:
repo-memory: true
---

# Test Workflow

This workflow tests GitHub Enterprise support.
`

if err := os.WriteFile(workflowPath, []byte(content), 0644); err != nil {
t.Fatalf("Failed to write workflow file: %v", err)
}

compiler := NewCompiler()
if err := compiler.CompileWorkflow(workflowPath); err != nil {
t.Fatalf("Failed to compile workflow: %v", err)
}

// Read the generated lock file
lockPath := stringutil.MarkdownToLockFile(workflowPath)
lockContent, err := os.ReadFile(lockPath)
if err != nil {
t.Fatalf("Failed to read lock file: %v", err)
}
lockFile := string(lockContent)

// Check that GITHUB_SERVER_URL is passed to clone step
if !strings.Contains(lockFile, "GITHUB_SERVER_URL: ${{ github.server_url }}") {
t.Error("Expected GITHUB_SERVER_URL environment variable in clone step")
}

// Check that GITHUB_SERVER_URL is passed to push step (in push_repo_memory job)
// The push step should include GITHUB_SERVER_URL in the env section
if !strings.Contains(lockFile, "GITHUB_SERVER_URL: ${{ github.server_url }}") {
t.Error("Expected GITHUB_SERVER_URL environment variable in push step")
Comment on lines +350 to +353
Copy link

Copilot AI Feb 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test checks for GITHUB_SERVER_URL twice with identical conditions (lines 346 and 352), which doesn't distinguish between the clone step and push step. Consider making these checks more specific by verifying the context around each occurrence (e.g., checking for nearby step names) to ensure both steps actually have the environment variable set.

Suggested change
// Check that GITHUB_SERVER_URL is passed to push step (in push_repo_memory job)
// The push step should include GITHUB_SERVER_URL in the env section
if !strings.Contains(lockFile, "GITHUB_SERVER_URL: ${{ github.server_url }}") {
t.Error("Expected GITHUB_SERVER_URL environment variable in push step")
// Check that GITHUB_SERVER_URL is passed to both clone and push steps
// It should appear at least twice (once for the clone step and once for the push step)
if strings.Count(lockFile, "GITHUB_SERVER_URL: ${{ github.server_url }}") < 2 {
t.Error("Expected GITHUB_SERVER_URL environment variable in both clone and push steps")

Copilot uses AI. Check for mistakes.
}

// Verify that hardcoded github.com is NOT in git URLs
// The workflow should NOT contain hardcoded github.com URLs in git commands
if strings.Contains(lockFile, "@github.com/") {
t.Error("Found hardcoded @github.com in workflow - should use dynamic server URL")
}

// Check for the shell script that uses GITHUB_SERVER_URL
if !strings.Contains(lockFile, "bash /opt/gh-aw/actions/clone_repo_memory_branch.sh") {
t.Error("Expected clone_repo_memory_branch.sh script invocation")
}
}