Skip to content

Scion/harness journey p1#447

Merged
ptone merged 11 commits into
GoogleCloudPlatform:mainfrom
ptone:scion/harness-journey-p1
Jun 18, 2026
Merged

Scion/harness journey p1#447
ptone merged 11 commits into
GoogleCloudPlatform:mainfrom
ptone:scion/harness-journey-p1

Conversation

@ptone

@ptone ptone commented Jun 18, 2026

Copy link
Copy Markdown
Member

Fixes #<issue_number_goes_here>

It's a good idea to open an issue first for discussion.

  • Tests pass
  • Appropriate changes to documentation are included in the PR

@google-cla

google-cla Bot commented Jun 18, 2026

Copy link
Copy Markdown

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request introduces the ability to re-import and update harness configurations and templates from their remote source URLs. It adds a new harness-config update CLI command, a POST /api/v1/harness-configs/{id}/reimport API endpoint, database schema updates to store the source_url, and a "Refresh from Source" button in the frontend UI. The review feedback highlights several safety and robustness improvements, including using cmd.Context() to respect CLI cancellation signals, avoiding the r.ContentLength anti-pattern when parsing HTTP request bodies, and adding nil checks for database and API client responses to prevent potential nil pointer dereferences.

Comment on lines +723 to +729
var req ReimportHarnessConfigRequest
if r.ContentLength > 0 {
if err := readJSON(r, &req); err != nil {
BadRequest(w, "Invalid request body: "+err.Error())
return
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

Checking r.ContentLength > 0 to decide whether to parse the request body is an anti-pattern. In HTTP/1.1 with chunked transfer encoding or in HTTP/2, r.ContentLength can be -1 (indicating unknown length), which would cause the request body to be silently ignored. Instead, check if r.Body is not nil and not http.NoBody before decoding.

Suggested change
var req ReimportHarnessConfigRequest
if r.ContentLength > 0 {
if err := readJSON(r, &req); err != nil {
BadRequest(w, "Invalid request body: "+err.Error())
return
}
}
var req ReimportHarnessConfigRequest
if r.Body != nil && r.Body != http.NoBody {
if err := readJSON(r, &req); err != nil {
BadRequest(w, "Invalid request body: "+err.Error())
return
}
}

Comment on lines +717 to +721
hc, err := s.store.GetHarnessConfig(ctx, id)
if err != nil {
writeErrorFromErr(w, err, "")
return
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

Add a nil check for hc to prevent a potential nil pointer dereference if GetHarnessConfig returns nil, nil.

	hc, err := s.store.GetHarnessConfig(ctx, id)
	if err != nil {
		writeErrorFromErr(w, err, "")
		return
	}
	if hc == nil {
		writeError(w, http.StatusNotFound, "not_found", "Harness config not found", nil)
		return
	}

Comment thread cmd/harness_config_update.go Outdated
Comment on lines +75 to +76
ctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)
defer cancel()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

Use cmd.Context() instead of context.Background() to respect cancellation signals (such as Ctrl+C) handled by Cobra.

Suggested change
ctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)
defer cancel()
ctx, cancel := context.WithTimeout(cmd.Context(), 120*time.Second)
defer cancel()

Comment on lines +91 to +93
if err != nil {
return fmt.Errorf("failed to search Hub: %w", err)
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

Add a nil check for resp to prevent a potential nil pointer dereference if the API client returns a nil response without an error.

	if err != nil {
		return fmt.Errorf("failed to search Hub: %w", err)
	}
	if resp == nil {
		return fmt.Errorf("failed to search Hub: received empty response")
	}

Comment on lines +144 to +146
if err != nil {
return fmt.Errorf("failed to list harness-configs: %w", err)
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

Add a nil check for resp to prevent a potential nil pointer dereference if the API client returns a nil response without an error.

Suggested change
if err != nil {
return fmt.Errorf("failed to list harness-configs: %w", err)
}
if err != nil {
return fmt.Errorf("failed to list harness-configs: %w", err)
}
if resp == nil {
return fmt.Errorf("failed to list harness-configs: received empty response")
}

@ptone ptone force-pushed the scion/harness-journey-p1 branch from 0414107 to 69f09cd Compare June 18, 2026 22:32
@ptone

ptone commented Jun 18, 2026

Copy link
Copy Markdown
Member Author

/gemini review

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request introduces the ability to re-import and update harness configurations from their remote source URLs. It adds a new harness-config update CLI command, a backend API endpoint /api/v1/harness-configs/{id}/reimport, and a frontend "Refresh from Source" button. Additionally, the source_url field is integrated into the HarnessConfig and Template database schemas. The review feedback highlights two main improvements in cmd/harness_config_update.go: handling errors when resolving the project directory instead of silently ignoring them, and adding a nil check on the re-import result to prevent a potential nil pointer dereference.

Comment on lines +121 to +124
result, err := hubCtx.Client.HarnessConfigs().Reimport(ctx, match.ID, urlOverride)
if err != nil {
return fmt.Errorf("reimport failed: %w", err)
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

high

The Reimport call can potentially return a nil result even if err is nil. To prevent a potential nil pointer dereference panic when accessing result.Count or result.HarnessConfigs, add a nil check for result.

result, err := hubCtx.Client.HarnessConfigs().Reimport(ctx, match.ID, urlOverride)
	if err != nil {
		return fmt.Errorf("reimport failed: %w", err)
	}
	if result == nil {
		return fmt.Errorf("reimport failed: received empty response from Hub")
	}

Comment on lines +55 to +63
var gp string
if projectPath != "" {
resolved, err := config.GetResolvedProjectDir(projectPath)
if err == nil {
gp = resolved
}
} else if projectDir, err := config.GetResolvedProjectDir(""); err == nil {
gp = projectDir
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

If projectPath is explicitly provided by the user, any error encountered while resolving the project directory should be handled and returned rather than silently ignored. Silently ignoring the error can lead to unexpected behavior or confusing error messages later in the execution.

Suggested change
var gp string
if projectPath != "" {
resolved, err := config.GetResolvedProjectDir(projectPath)
if err == nil {
gp = resolved
}
} else if projectDir, err := config.GetResolvedProjectDir(""); err == nil {
gp = projectDir
}
var gp string
if projectPath != "" {
resolved, err := config.GetResolvedProjectDir(projectPath)
if err != nil {
return fmt.Errorf("failed to resolve project directory %q: %w", projectPath, err)
}
gp = resolved
} else if projectDir, err := config.GetResolvedProjectDir(""); err == nil {
gp = projectDir
}

@ptone ptone force-pushed the scion/harness-journey-p1 branch from 91c9d15 to 73daa89 Compare June 18, 2026 22:49
Scion Agent (harness-journey-p1-dev) added 11 commits June 18, 2026 22:49
Add optional source_url field to track the import origin URL for
harness-configs and templates, enabling reimport/update flows.
… pipeline

Add SourceURL field to store.HarnessConfig, store.Template,
ResourceRecord, and the ent adapter CRUD operations. Update
Bootstrap() to accept sourceURL parameter and persist it on
create/update. Thread sourceURL from resourceDir through the
import worker loop.
- POST /api/v1/harness-configs/{id}/reimport endpoint that re-imports
  from stored source_url or an override URL
- CLI: scion harness-config update <name> [--url] [--all]
- Hub client: Reimport() method on HarnessConfigService
- Show command now displays source URL when present
- Display source URL as clickable link in metadata row
- Show Refresh from Source button when sourceUrl is present
- Button calls POST /api/v1/harness-configs/{id}/reimport
- Reload config details after successful reimport
The mock was missing the new Reimport method added to the
HarnessConfigService interface, causing CI lint failures.
Remove WithDropColumn and WithDropIndex from Ent auto-migration.
These flags cause SQLite to silently drop data during table recreation
when schema changes occur between versions, which was the root cause
of URL-imported harness configs disappearing after server restarts.
The importFromRemote signature gained a nameFilter []string parameter
upstream. Pass nil for the reimport endpoint since it reimports all
resources from the source URL.
Without a no_auth section, selecting "no authentication" for the
antigravity harness causes the container to start with the full
agy-wrapper.sh command (dbus + gnome-keyring + AGY) instead of
dropping to a shell. This fails because AGY has no credentials.

Add no_auth behavior matching claude/gemini/codex/opencode harnesses.
C-1: Add user-scope authorization check in reimport handler. Without
this, any authenticated user could reimport another user's harness
config. Also add default-deny for unknown scopes.

M-1: Properly handle malformed JSON in reimport request body instead
of silently discarding parse errors.

H-1/H-2: Gate human-readable printf calls behind !isJSONOutput() in
the CLI update command so --format json produces clean structured
output.
- Use r.Body != nil && r.Body != http.NoBody instead of r.ContentLength
  for chunked/HTTP2 compatibility in reimport handler
- Add nil check for harness config after GetHarnessConfig lookup
- Use cmd.Context() instead of context.Background() in update command
- Add nil checks for resp after Hub List calls in both update functions
- Add nil check for result after Reimport() call to prevent panic on
  (nil, nil) return
- Return error when user-provided projectPath fails to resolve instead
  of silently ignoring it
@ptone ptone force-pushed the scion/harness-journey-p1 branch from 73daa89 to 4fc7f1b Compare June 18, 2026 22:50
@ptone ptone merged commit 3fa3291 into GoogleCloudPlatform:main Jun 18, 2026
6 of 8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant