Initial public release of Warp.
Repo-Sync-Origin: warpdotdev/warp-internal@12af1d983b
This commit is contained in:
@@ -0,0 +1,117 @@
|
|||||||
|
@{
|
||||||
|
Severity = @(
|
||||||
|
'Error'
|
||||||
|
'Warning'
|
||||||
|
'Information'
|
||||||
|
)
|
||||||
|
CustomRulePath = @(
|
||||||
|
'PSScriptAnalyzerCustomRules.psm1'
|
||||||
|
)
|
||||||
|
IncludeDefaultRules = $true
|
||||||
|
ExcludeRules = @(
|
||||||
|
# In an ideal world we'd keep this on and have people opt out on a variable
|
||||||
|
# by variable basis, but PSScriptAnalyzer does not have that degree of control.
|
||||||
|
'PSAvoidGlobalVars'
|
||||||
|
# Normally we'd disable this in-line, but there are issues with using inline
|
||||||
|
# diagnostic controls in pwsh_init_shell.ps1
|
||||||
|
'PSAvoidUsingWriteHost'
|
||||||
|
# TODO(CORE-2985): Evaluate if we want to turn this on.
|
||||||
|
# Disabling this for now, most of our Warp functions should not be invoked by
|
||||||
|
# users directly anyway.
|
||||||
|
'PSProvideCommentHelp'
|
||||||
|
)
|
||||||
|
Rules = @{
|
||||||
|
'Test-StringEscapeCode' = @{
|
||||||
|
Enable = $true
|
||||||
|
}
|
||||||
|
PSAvoidExclaimOperator = @{
|
||||||
|
Enable = $true
|
||||||
|
}
|
||||||
|
|
||||||
|
PSAvoidSemicolonsAsLineTerminators = @{
|
||||||
|
Enable = $true
|
||||||
|
}
|
||||||
|
|
||||||
|
PSAvoidUsingDoubleQuotesForConstantString = @{
|
||||||
|
Enable = $true
|
||||||
|
}
|
||||||
|
|
||||||
|
PSPlaceOpenBrace = @{
|
||||||
|
Enable = $true
|
||||||
|
OnSameLine = $true
|
||||||
|
NewLineAfter = $true
|
||||||
|
IgnoreOneLineBlock = $true
|
||||||
|
}
|
||||||
|
|
||||||
|
PSPlaceCloseBrace = @{
|
||||||
|
Enable = $true
|
||||||
|
NewLineAfter = $false
|
||||||
|
IgnoreOneLineBlock = $true
|
||||||
|
NoEmptyLineBefore = $false
|
||||||
|
}
|
||||||
|
|
||||||
|
PSUseConsistentIndentation = @{
|
||||||
|
Enable = $true
|
||||||
|
Kind = 'space'
|
||||||
|
PipelineIndentation = 'IncreaseIndentationForFirstPipeline'
|
||||||
|
IndentationSize = 4
|
||||||
|
}
|
||||||
|
|
||||||
|
PSUseConsistentWhitespace = @{
|
||||||
|
Enable = $true
|
||||||
|
CheckInnerBrace = $true
|
||||||
|
CheckOpenBrace = $false
|
||||||
|
CheckOpenParen = $false
|
||||||
|
CheckOperator = $false
|
||||||
|
CheckPipe = $true
|
||||||
|
CheckPipeForRedundantWhitespace = $false
|
||||||
|
CheckSeparator = $true
|
||||||
|
CheckParameter = $false
|
||||||
|
}
|
||||||
|
|
||||||
|
PSUseCorrectCasing = @{
|
||||||
|
Enable = $true
|
||||||
|
}
|
||||||
|
|
||||||
|
PSAlignAssignmentStatement = @{
|
||||||
|
Enable = $false
|
||||||
|
CheckHashtable = $false
|
||||||
|
}
|
||||||
|
|
||||||
|
PSUseCompatibleSyntax = @{
|
||||||
|
Enable = $true
|
||||||
|
TargetVersions = @('5.1', '6.2', '7.2')
|
||||||
|
}
|
||||||
|
|
||||||
|
PSUseCompatibleCommands = @{
|
||||||
|
Enable = $true
|
||||||
|
TargetProfiles = @(
|
||||||
|
# Windows 10 Powershell 5
|
||||||
|
'win-48_x64_10.0.17763.0_5.1.17763.316_x64_4.0.30319.42000_framework'
|
||||||
|
# Windows 10 Powershell 6
|
||||||
|
'win-4_x64_10.0.18362.0_6.2.4_x64_4.0.30319.42000_core'
|
||||||
|
# Windows 10 Powershell 7
|
||||||
|
'win-4_x64_10.0.18362.0_7.0.0_x64_3.1.2_core'
|
||||||
|
# Ubuntu Powershell 6
|
||||||
|
'ubuntu_x64_18.04_6.2.4_x64_4.0.30319.42000_core'
|
||||||
|
# Ubuntu Powershell 7
|
||||||
|
'ubuntu_x64_18.04_7.0.0_x64_3.1.2_core'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
PSUseCompatibleTypes = @{
|
||||||
|
Enable = $true
|
||||||
|
TargetProfiles = @(
|
||||||
|
# Windows 10 Powershell 5
|
||||||
|
'win-48_x64_10.0.17763.0_5.1.17763.316_x64_4.0.30319.42000_framework'
|
||||||
|
# Windows 10 Powershell 6
|
||||||
|
'win-4_x64_10.0.18362.0_6.2.4_x64_4.0.30319.42000_core'
|
||||||
|
# Windows 10 Powershell 7
|
||||||
|
'win-4_x64_10.0.18362.0_7.0.0_x64_3.1.2_core'
|
||||||
|
# Ubuntu Powershell 6
|
||||||
|
'ubuntu_x64_18.04_6.2.4_x64_4.0.30319.42000_core'
|
||||||
|
# Ubuntu Powershell 7
|
||||||
|
'ubuntu_x64_18.04_7.0.0_x64_3.1.2_core'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,102 @@
|
|||||||
|
---
|
||||||
|
name: add-feature-flag
|
||||||
|
description: Add a new feature flag to gate code changes in the Warp codebase.
|
||||||
|
---
|
||||||
|
|
||||||
|
# add-feature-flag
|
||||||
|
|
||||||
|
Add a new feature flag to gate code changes in the Warp codebase.
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
Feature flags in Warp are compile-time flags that allow features to be selectively enabled for different channels (e.g.: Dev, Stable). They use a small runtime plumbing layer that checks if a flag is enabled.
|
||||||
|
|
||||||
|
## Steps
|
||||||
|
|
||||||
|
### 1. Add to Cargo.toml
|
||||||
|
Add the feature to `app/Cargo.toml` under the `[features]` section, but **NOT** under the `default` nested stanza:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[features]
|
||||||
|
your_feature_name = []
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Add to FeatureFlag enum
|
||||||
|
Add a new variant to the `FeatureFlag` enum in `warp_core/src/features.rs`:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
#[derive(Sequence)]
|
||||||
|
pub enum FeatureFlag {
|
||||||
|
YourFeatureName,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Add conditional compilation directive
|
||||||
|
Add the feature to `app/src/lib.rs` with a corresponding `#[cfg(feature = "...")]` attribute to ensure it's only included when enabled:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
#[cfg(feature = "your_feature_name")]
|
||||||
|
YourFeatureName,
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Gate code with runtime checks
|
||||||
|
In your code, use the runtime check to conditionally execute feature-gated code:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
if FeatureFlag::YourFeatureName.is_enabled() {
|
||||||
|
// feature-gated behavior
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. (Optional) Enable for dogfood builds
|
||||||
|
To enable the feature by default for Dev/dogfood builds, add it to the `DOGFOOD_FLAGS` array in `features.rs`:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
pub const DOGFOOD_FLAGS: &[FeatureFlag] = &[
|
||||||
|
FeatureFlag::YourFeatureName,
|
||||||
|
];
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6. Running with feature flags
|
||||||
|
To test locally with the feature enabled:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo run --features your_feature_name
|
||||||
|
|
||||||
|
# Multiple features:
|
||||||
|
cargo run --features your_feature_name,another_feature
|
||||||
|
```
|
||||||
|
|
||||||
|
## Keybindings with Feature Flags
|
||||||
|
|
||||||
|
If adding an `EditableBinding` or `FixedBinding` that's part of a gated feature, include an enabled predicate that checks the feature flag. This prevents the keybinding from appearing in keyboard settings when the feature is disabled.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
```rust
|
||||||
|
EditableBinding::new(
|
||||||
|
"action:name",
|
||||||
|
"Action description",
|
||||||
|
YourAction::Variant
|
||||||
|
)
|
||||||
|
.with_enabled(|| FeatureFlag::YourFeatureName.is_enabled())
|
||||||
|
.with_key_binding("cmdorctrl-key")
|
||||||
|
```
|
||||||
|
|
||||||
|
## Rolling Out to Stable
|
||||||
|
|
||||||
|
When ready to enable the feature for all Warp Stable users, add it to the `default` array in `app/Cargo.toml`:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[features]
|
||||||
|
default = [
|
||||||
|
"your_feature_name",
|
||||||
|
# other default features...
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Best Practices
|
||||||
|
|
||||||
|
- **Prefer runtime checks over cfg directives**: Use `FeatureFlag::YourFeatureName.is_enabled()` instead of `#[cfg(...)]` when possible, so flags can be toggled without recompilation and are easier to clean up later
|
||||||
|
- Use `#[cfg(...)]` only when code cannot compile without the flag (e.g., platform-specific code or missing dependencies)
|
||||||
|
- Keep flags high-level and product-focused rather than per-call-site
|
||||||
|
- Remove flags and dead branches after launch has stabilized
|
||||||
@@ -0,0 +1,167 @@
|
|||||||
|
---
|
||||||
|
name: add-telemetry
|
||||||
|
description: Add telemetry events to track user behavior or system events in the Warp codebase. Use when instrumenting new features, debugging issues, or measuring product metrics.
|
||||||
|
---
|
||||||
|
|
||||||
|
# add-telemetry
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
Warp uses a trait-based telemetry system where feature-specific enums implement the `TelemetryEvent` trait. This approach keeps telemetry events organized by domain rather than in one giant enum.
|
||||||
|
|
||||||
|
**Important**: Before implementing telemetry, collaborate with the user to:
|
||||||
|
- Define what events should be tracked and when
|
||||||
|
- Determine what data should be included in each event
|
||||||
|
- Clarify the purpose and expected usage of the telemetry
|
||||||
|
|
||||||
|
Adding telemetry code is straightforward, but designing meaningful instrumentation requires careful thought.
|
||||||
|
|
||||||
|
## Steps
|
||||||
|
|
||||||
|
### 1. Identify or create a telemetry module
|
||||||
|
|
||||||
|
Find an existing feature-specific telemetry file (e.g., `app/src/antivirus/telemetry.rs`) or create a new one for your feature area.
|
||||||
|
|
||||||
|
### 2. Define the telemetry event enum
|
||||||
|
|
||||||
|
Add a new variant to an enum that implements `TelemetryEvent`, or create a new enum:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use serde_json::{json, Value};
|
||||||
|
use strum_macros::{EnumDiscriminants, EnumIter};
|
||||||
|
use warp_core::telemetry::{EnablementState, TelemetryEvent, TelemetryEventDesc};
|
||||||
|
|
||||||
|
#[derive(Debug, EnumDiscriminants)]
|
||||||
|
#[strum_discriminants(derive(EnumIter))]
|
||||||
|
pub enum YourFeatureTelemetryEvent {
|
||||||
|
ActionStarted {
|
||||||
|
duration_ms: u64,
|
||||||
|
},
|
||||||
|
ActionCompleted {
|
||||||
|
success: bool,
|
||||||
|
error: Option<String>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Implement the TelemetryEvent trait
|
||||||
|
|
||||||
|
`EnablementState` allows you to control when events are sent:
|
||||||
|
|
||||||
|
- `EnablementState::Always` - Always send the event
|
||||||
|
- `EnablementState::Flag(FeatureFlag::YourFeature)` - Only send when the feature flag is enabled
|
||||||
|
- `EnablementState::Channel(Channel::Dev)` - Only send in specific build channels
|
||||||
|
|
||||||
|
```rust
|
||||||
|
impl TelemetryEvent for YourFeatureTelemetryEvent {
|
||||||
|
fn name(&self) -> &'static str {
|
||||||
|
YourFeatureTelemetryEventDiscriminants::from(self).name()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn payload(&self) -> Option<Value> {
|
||||||
|
match self {
|
||||||
|
Self::ActionStarted { duration_ms } => Some(json!({
|
||||||
|
"duration_ms": duration_ms,
|
||||||
|
})),
|
||||||
|
Self::ActionCompleted { success, error } => Some(json!({
|
||||||
|
"success": success,
|
||||||
|
"error": error,
|
||||||
|
})),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn description(&self) -> &'static str {
|
||||||
|
YourFeatureTelemetryEventDiscriminants::from(self).description()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn enablement_state(&self) -> EnablementState {
|
||||||
|
YourFeatureTelemetryEventDiscriminants::from(self).enablement_state()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn contains_ugc(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
Self::ActionStarted { .. } => false,
|
||||||
|
Self::ActionCompleted { .. } => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn event_descs() -> impl Iterator<Item = Box<dyn TelemetryEventDesc>> {
|
||||||
|
warp_core::telemetry::enum_events::<Self>()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Implement TelemetryEventDesc for the discriminants
|
||||||
|
|
||||||
|
```rust
|
||||||
|
impl TelemetryEventDesc for YourFeatureTelemetryEventDiscriminants {
|
||||||
|
fn name(&self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
Self::ActionStarted => "YourFeature.Action.Started",
|
||||||
|
Self::ActionCompleted => "YourFeature.Action.Completed",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn description(&self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
Self::ActionStarted => "User started the action",
|
||||||
|
Self::ActionCompleted => "User completed the action",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn enablement_state(&self) -> EnablementState {
|
||||||
|
match self {
|
||||||
|
Self::ActionStarted | Self::ActionCompleted => EnablementState::Always,
|
||||||
|
// Or gate behind a feature flag:
|
||||||
|
// EnablementState::Flag(FeatureFlag::YourFeature)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. Register the telemetry event
|
||||||
|
|
||||||
|
At the end of your telemetry module, register the event:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
warp_core::register_telemetry_event!(YourFeatureTelemetryEvent);
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6. Send telemetry events from your code
|
||||||
|
|
||||||
|
Use `send_telemetry_from_ctx!` in views or models with a `ViewContext` or `ModelContext`:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use warp_core::send_telemetry_from_ctx;
|
||||||
|
|
||||||
|
// In a view update or model method
|
||||||
|
send_telemetry_from_ctx!(
|
||||||
|
YourFeatureTelemetryEvent::ActionStarted {
|
||||||
|
duration_ms: 150,
|
||||||
|
},
|
||||||
|
ctx
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
For code with only `AppContext`, use `send_telemetry_from_app_ctx!` instead.
|
||||||
|
|
||||||
|
### 7. Test locally
|
||||||
|
|
||||||
|
Run Warp with the `log_named_telemetry_events` feature flag to see telemetry events logged to the console:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo run --features log_named_telemetry_events
|
||||||
|
```
|
||||||
|
|
||||||
|
## Best Practices
|
||||||
|
|
||||||
|
- Keep telemetry enums feature-specific rather than adding to a global enum
|
||||||
|
- Set `contains_ugc()` to `true` if the payload includes user-generated content
|
||||||
|
- Use descriptive event names following the pattern `Feature.Action.Result`
|
||||||
|
- Include only necessary data in payloads to minimize bandwidth and storage
|
||||||
|
- Consider privacy implications when deciding what data to include
|
||||||
|
- Avoid exhaustive matching with wildcards; handle all variants explicitly
|
||||||
|
|
||||||
|
## Example Reference
|
||||||
|
|
||||||
|
See `app/src/antivirus/telemetry.rs` for a complete example of a feature-specific telemetry implementation.
|
||||||
@@ -0,0 +1,231 @@
|
|||||||
|
---
|
||||||
|
name: create-pr
|
||||||
|
description: Create a pull request in the warp repository for the current branch. Use when the user mentions opening a PR, creating a pull request, submitting changes for review, or preparing code for merge.
|
||||||
|
---
|
||||||
|
|
||||||
|
# create-pr
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
This guide covers best practices for creating pull requests in the warp repository, including merging master, running presubmit checks, linking Linear tasks, ensuring appropriate test coverage, and structuring your PR for effective review.
|
||||||
|
|
||||||
|
## Related Skills
|
||||||
|
|
||||||
|
- `fix-errors` - Fix presubmit failures (formatting, linting, tests) before opening PR
|
||||||
|
- `rust-unit-tests` - Write unit tests for your changes, if applicable (see "Testing Requirements" below)
|
||||||
|
- `warp-integration-test` - Add or update integration coverage for user-visible flows, regressions, and P0 use cases
|
||||||
|
- `add-feature-flag` - Gate changes behind feature flags
|
||||||
|
|
||||||
|
## Pre-PR Checklist
|
||||||
|
|
||||||
|
### 1. Merge master into your feature branch
|
||||||
|
|
||||||
|
**Always merge master into your feature branch before starting the review process.**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git fetch origin
|
||||||
|
git merge origin/master
|
||||||
|
```
|
||||||
|
|
||||||
|
Resolve any merge conflicts locally before opening the PR.
|
||||||
|
|
||||||
|
### 2. Run presubmit checks for code changes
|
||||||
|
|
||||||
|
If the PR includes code changes, run the relevant presubmit checks before opening or updating it:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./script/presubmit
|
||||||
|
```
|
||||||
|
|
||||||
|
`./script/presubmit` runs:
|
||||||
|
- `cargo fmt` - Code formatting
|
||||||
|
- `cargo clippy` - Linting with all warnings as errors
|
||||||
|
- All tests (unit, doc, and integration)
|
||||||
|
If the PR is documentation-only (for example, skills, markdown, or other non-code content), you do not need to run `cargo fmt` or `cargo clippy` just to open or update the PR.
|
||||||
|
|
||||||
|
If presubmit fails for a code-changing PR, use the `fix-errors` skill to resolve issues.
|
||||||
|
|
||||||
|
**You must run `cargo fmt` and `cargo clippy` before:**
|
||||||
|
- Opening a new PR that includes code changes
|
||||||
|
- Pushing new commits that include code changes to an existing PR branch
|
||||||
|
- Any reviewed branch update that changes code
|
||||||
|
|
||||||
|
### 3. Review your changes
|
||||||
|
|
||||||
|
Before creating a PR, review what changes you're about to submit:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# View commits in your branch (comparing against base branch)
|
||||||
|
git --no-pager log <base-branch>..HEAD --oneline
|
||||||
|
|
||||||
|
# View file statistics for changes
|
||||||
|
git --no-pager diff <base-branch>...HEAD --stat
|
||||||
|
|
||||||
|
# View full diff
|
||||||
|
git --no-pager diff <base-branch>...HEAD
|
||||||
|
```
|
||||||
|
|
||||||
|
This helps you:
|
||||||
|
- Verify all intended changes are included
|
||||||
|
- Catch unintended changes before review
|
||||||
|
- Write an accurate PR description
|
||||||
|
- Ensure you're comparing against the correct base branch
|
||||||
|
- **Tests:** Include tests when required—bug fixes (regression test), algorithmic code (unit tests), UI components (layout test), P0 use cases (integration test). See Testing Requirements below.
|
||||||
|
|
||||||
|
### 4. Link to Linear task
|
||||||
|
|
||||||
|
When possible, PRs should be associated with a Linear task. Use the Linear MCP tool (if available) to find corresponding issues.
|
||||||
|
|
||||||
|
**Branch naming convention:**
|
||||||
|
Remote branches should be prefixed with your name (e.g., `zheng/feature`, `alice/fix-bug`).
|
||||||
|
|
||||||
|
**How to link PRs to Linear:**
|
||||||
|
Include the issue ID in the PR title (e.g., `[WARP-1234] Add new feature`). Do this **before** creating the PR for automatic linking.
|
||||||
|
|
||||||
|
### 5. Open the PR
|
||||||
|
|
||||||
|
Use the PR template at `.github/pull_request_template.md` when opening PRs.
|
||||||
|
|
||||||
|
Add changelog entries when appropriate using the format at the bottom of the PR template. Some examples:
|
||||||
|
- Feature: "Global search in files across your current directories. Use CMD-F/CTRL-SHIFT-F to open."
|
||||||
|
- Improvement: "Added horizontal autoscrolling when jumping to line/column."
|
||||||
|
- Bug fix: "Fixed session viewer input being cleared when agent runs commands.
|
||||||
|
|
||||||
|
**CLI workflow:**
|
||||||
|
|
||||||
|
- **Check if PR exists** for current branch:
|
||||||
|
```bash
|
||||||
|
gh pr view --json number,url
|
||||||
|
```
|
||||||
|
Exit code 0 if PR exists, 1 if not.
|
||||||
|
|
||||||
|
- **Create a new PR:**
|
||||||
|
```bash
|
||||||
|
# With title and body
|
||||||
|
gh pr create --title "Title" --body "Description" --draft
|
||||||
|
|
||||||
|
# Auto-fill from commits
|
||||||
|
gh pr create --fill --draft
|
||||||
|
|
||||||
|
# Use PR template file
|
||||||
|
gh pr create --body-file .github/pull_request_template.md --title "Title" --draft
|
||||||
|
```
|
||||||
|
Key flags: `--draft` / `-d`, `--fill` / `-f`, `--body-file` / `-F`, `--web` / `-w`
|
||||||
|
|
||||||
|
- **Update an existing PR:**
|
||||||
|
```bash
|
||||||
|
gh pr edit --title "New title" --body "New body"
|
||||||
|
gh pr edit --add-reviewer username --add-label bug
|
||||||
|
```
|
||||||
|
|
||||||
|
- **Mark PR ready for review:**
|
||||||
|
```bash
|
||||||
|
gh pr ready
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6. Include co-author attribution
|
||||||
|
|
||||||
|
When committing changes or creating a PR, include attribution at the end of every commit message or PR description:
|
||||||
|
|
||||||
|
```
|
||||||
|
Co-Authored-By: Warp <agent@warp.dev>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Testing Requirements
|
||||||
|
|
||||||
|
### Bug fixes require regression tests
|
||||||
|
|
||||||
|
**All bug fixes should be accompanied by a regression test.** This helps prevent re-breaking something that was already broken once.
|
||||||
|
|
||||||
|
The test should:
|
||||||
|
- Reproduce the original bug (would fail before the fix)
|
||||||
|
- Pass after the fix is applied
|
||||||
|
- Be clearly named to indicate what bug it's preventing
|
||||||
|
|
||||||
|
### Algorithmic code requires unit tests
|
||||||
|
|
||||||
|
Code with non-trivial logic should have unit tests to validate functionality:
|
||||||
|
|
||||||
|
**Examples of what needs unit tests:**
|
||||||
|
- Custom data structures (e.g., `SumTree`)
|
||||||
|
- Search-related APIs that should return expected results for a given query
|
||||||
|
- Core layout code in the UI framework
|
||||||
|
- Any algorithmic or computational logic
|
||||||
|
|
||||||
|
**Not required for:**
|
||||||
|
- Sufficiently-simple functions
|
||||||
|
- Trivial getters/setters
|
||||||
|
|
||||||
|
See the `rust-unit-tests` skill for guidance on writing unit tests.
|
||||||
|
|
||||||
|
### UI components need layout validation tests
|
||||||
|
|
||||||
|
**All UI components (implementations of `View`) should have a simple unit test** to validate that they can be laid out without a panic.
|
||||||
|
|
||||||
|
This provides high-level coverage over rendering "safety" (though not "correctness"):
|
||||||
|
|
||||||
|
```rust
|
||||||
|
#[test]
|
||||||
|
fn test_component_can_layout() {
|
||||||
|
use warpui::App;
|
||||||
|
use warp::test_util::{terminal::initialize_app_for_terminal_view, add_window_with_terminal};
|
||||||
|
|
||||||
|
App::test((), |mut app| async move {
|
||||||
|
initialize_app_for_terminal_view(&mut app);
|
||||||
|
let term = add_window_with_terminal(&mut app, None);
|
||||||
|
|
||||||
|
// Render the component - should not panic
|
||||||
|
term.update(&mut app, |view, ctx| {
|
||||||
|
// Create and layout your component
|
||||||
|
});
|
||||||
|
})
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Ask before skipping integration coverage
|
||||||
|
|
||||||
|
If the PR changes a user-visible flow, fixes an end-to-end regression, or otherwise looks like it would benefit from integration coverage, use the `ask_user_question` tool before creating or updating the PR to ask whether the user wants an integration test added as part of the work.
|
||||||
|
|
||||||
|
Prefer a direct choice such as:
|
||||||
|
|
||||||
|
- `Yes, add an integration test before creating the PR`
|
||||||
|
- `No, continue without an integration test`
|
||||||
|
|
||||||
|
If the user chooses to add one, use the `warp-integration-test` skill.
|
||||||
|
|
||||||
|
### P0 use cases require integration tests
|
||||||
|
|
||||||
|
**All "P0 use cases" require an integration test** that covers the behavior/flow in question.
|
||||||
|
|
||||||
|
**A "P0 use case" is defined as:** Any behavior of the application that, if broken, warrants an out-of-band release.
|
||||||
|
|
||||||
|
Integration tests should:
|
||||||
|
- Exercise the full user-facing flow
|
||||||
|
- Validate end-to-end functionality
|
||||||
|
- Be placed in the `integration/` directory
|
||||||
|
|
||||||
|
Use the `warp-integration-test` skill for implementation details, test registration steps, and validation workflow.
|
||||||
|
|
||||||
|
## PR Description Guidelines
|
||||||
|
|
||||||
|
Your PR summary under the "Description" section should include:
|
||||||
|
|
||||||
|
1. **What** - What changes are being made
|
||||||
|
2. **Why** - Why these changes are necessary (link to Linear task if applicable)
|
||||||
|
3. **How** - Brief explanation of the approach taken
|
||||||
|
|
||||||
|
## After Opening the PR
|
||||||
|
|
||||||
|
1. **Monitor CI checks** - Ensure all automated checks pass
|
||||||
|
2. **Respond to review comments** - Address feedback promptly
|
||||||
|
3. **Keep the PR up to date** - Merge master if conflicts arise
|
||||||
|
4. **Re-run relevant validation** - After making changes based on review feedback. For code changes, re-run `cargo fmt`/`cargo clippy` (and other relevant checks); for documentation-only changes, this is not required.
|
||||||
|
|
||||||
|
## Best Practices
|
||||||
|
|
||||||
|
- **Keep PRs focused** - One logical change per PR when possible
|
||||||
|
- **Write clear commit messages** - Explain what and why, not just what
|
||||||
|
- **Self-review first** - Review your own diff before requesting review
|
||||||
|
- **Update tests** - Ensure test coverage reflects your changes
|
||||||
|
- **Document breaking changes** - Call out any API changes or breaking modifications
|
||||||
|
- **Use feature flags** - Gate risky changes behind feature flags when appropriate (see the `add-feature-flag` skill)
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
---
|
||||||
|
name: dedupe-issue-local
|
||||||
|
specializes: dedupe-issue
|
||||||
|
description: Repo-specific dedupe guidance for warp-external. Only the categories declared overridable by the core dedupe-issue skill may be specialized here.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Repo-specific dedupe guidance for `warp-external`
|
||||||
|
|
||||||
|
This file is a companion to the core `dedupe-issue` skill. It does not
|
||||||
|
redefine the duplicate-detection algorithm, the similarity thresholds,
|
||||||
|
or the output contract. It only specializes the override categories the
|
||||||
|
core skill marks as overridable.
|
||||||
|
|
||||||
|
## Repo-specific normalizations
|
||||||
|
|
||||||
|
- Strip low-signal title prefixes such as `Bug:`, `Feature:`, `Request:`, `[Bug]`, `[Feature]`, `Warp:`, and platform tags like `[macOS]`, `[Linux]`, or `[Windows]` before comparing titles.
|
||||||
|
- Treat app channel/version, OS version, and shell name as supporting evidence, not as duplicate blockers, when the core symptom and reproduction path are otherwise the same.
|
||||||
|
- Do not collapse distinct Warp surfaces just because they share a word like "agent", "terminal", "MCP", "settings", "search", or "sync". Require overlap in the actual failing behavior or requested capability.
|
||||||
|
- For terminal issues, compare shell/session context, command output behavior, prompt rendering, input behavior, and remote/tmux involvement before treating two reports as duplicates.
|
||||||
|
- For agent or MCP issues, compare the trigger path, local vs cloud execution, MCP server/tool, visible error, and expected workflow before treating two reports as duplicates.
|
||||||
|
- For UI/rendering issues, compare the affected surface and visible symptom. Similar screenshots or recordings are strong duplicate evidence when the title is vague.
|
||||||
|
|
||||||
|
## Known-duplicate clusters
|
||||||
|
|
||||||
|
No known-duplicate clusters have been captured for this repository yet. The weekly `update-dedupe` loop will propose additions here over time when maintainers repeatedly close issues as duplicates of the same canonical thread.
|
||||||
@@ -0,0 +1,112 @@
|
|||||||
|
---
|
||||||
|
name: diagnose-ci-failures
|
||||||
|
description: Diagnose CI failures for a PR using the GitHub CLI, extract error logs, and generate a plan to fix them. Use when the user asks to check CI status, pull CI issues, triage test failures, or investigate PR build failures.
|
||||||
|
---
|
||||||
|
|
||||||
|
# diagnose-ci-failures
|
||||||
|
|
||||||
|
Programmatically diagnose CI failures for a PR and generate a plan to fix them.
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
This skill provides a deterministic workflow to check CI status for a PR, extract failure logs, analyze errors, and create a plan (not code changes) to resolve issues. The output is always a plan document that can be reviewed before execution.
|
||||||
|
|
||||||
|
## Workflow
|
||||||
|
|
||||||
|
### 1. Verify PR exists for current branch
|
||||||
|
|
||||||
|
Get the current branch and check if a PR exists:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Get current branch
|
||||||
|
git branch --show-current
|
||||||
|
|
||||||
|
# Check for PR
|
||||||
|
gh --no-pager pr view <branch-name> --json number,title,url,state
|
||||||
|
```
|
||||||
|
|
||||||
|
If no PR exists, inform the user and offer to create one using the `create-pr` skill.
|
||||||
|
|
||||||
|
### 2. Check CI status
|
||||||
|
|
||||||
|
Fetch the status of all CI checks:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
gh pr view <branch-name> --json statusCheckRollup
|
||||||
|
```
|
||||||
|
|
||||||
|
Parse the output to identify:
|
||||||
|
- Completed checks vs. in-progress checks
|
||||||
|
- Successful checks
|
||||||
|
- Failed checks with their names and details URLs
|
||||||
|
|
||||||
|
If CI is still running, inform the user which checks have already failed or passed, highlight the checks that are still running, and suggest waiting for completion before diagnosis.
|
||||||
|
|
||||||
|
### 3. Extract failure logs
|
||||||
|
|
||||||
|
For each failed check, pull the logs using the run ID from the status check:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
gh run view <run-id> --log-failed
|
||||||
|
```
|
||||||
|
|
||||||
|
Focus on extracting:
|
||||||
|
- Error messages and their locations (file paths, line numbers)
|
||||||
|
- Compilation errors (unused imports, type mismatches, etc.)
|
||||||
|
- Linting/clippy errors with specific lint names
|
||||||
|
- Test failure messages and stack traces
|
||||||
|
- Build failures and their root causes
|
||||||
|
|
||||||
|
### 4. Categorize errors
|
||||||
|
|
||||||
|
Group errors by type:
|
||||||
|
- **Formatting issues**: `cargo fmt` failures
|
||||||
|
- **Linting issues**: `cargo clippy` warnings/errors
|
||||||
|
- **Compilation errors**: Type errors, missing imports, signature mismatches
|
||||||
|
- **Test failures**: Failing tests with their names and failure reasons
|
||||||
|
- **Platform-specific issues**: WASM, Linux, macOS, Windows-specific failures
|
||||||
|
|
||||||
|
### 5. Generate fix plan
|
||||||
|
|
||||||
|
Create a plan document (using `create_plan` tool) with:
|
||||||
|
- **Problem Statement**: Summary of failing checks
|
||||||
|
- **Current State**: What errors were found and where
|
||||||
|
- **Proposed Changes**: Specific fixes needed for each error category
|
||||||
|
- **Validation Steps**: Commands to verify fixes (fmt, clippy, tests, presubmit)
|
||||||
|
|
||||||
|
The plan should reference the `fix-errors` skill for detailed guidance on resolving specific error types.
|
||||||
|
|
||||||
|
## Important Notes
|
||||||
|
|
||||||
|
- **Always create a plan first**: Never make code changes directly. Generate a plan for user review
|
||||||
|
- **Check test status in CI**: Even if tests fail locally, verify they passed in CI before flagging as issues
|
||||||
|
- **Unrelated test failures**: If tests passed in CI but fail locally, they may be environment-specific or flaky
|
||||||
|
- **Multiple error types**: Fix one category at a time (e.g., all clippy errors before tests)
|
||||||
|
- **Cross-reference fix-errors skill**: For detailed error resolution strategies, use the `fix-errors` skill
|
||||||
|
|
||||||
|
## Common CI Check Names
|
||||||
|
|
||||||
|
- `Formatting + Clippy (MacOS)`
|
||||||
|
- `Formatting + Clippy (Linux)`
|
||||||
|
- `Run MacOS tests`
|
||||||
|
- `Run Linux tests`
|
||||||
|
- `Run Windows tests`
|
||||||
|
- `Check CI results` (summary check)
|
||||||
|
- `WASM build`
|
||||||
|
|
||||||
|
## Example Commands
|
||||||
|
|
||||||
|
**Get PR status with details:**
|
||||||
|
```bash
|
||||||
|
gh --no-pager pr view --json number,title,state,statusCheckRollup
|
||||||
|
```
|
||||||
|
|
||||||
|
**Get logs from specific failed run:**
|
||||||
|
```bash
|
||||||
|
gh run view 12345678 --log-failed
|
||||||
|
```
|
||||||
|
|
||||||
|
**Check for specific error in logs:**
|
||||||
|
```bash
|
||||||
|
gh run view 12345678 --log-failed 2>&1 | grep -A 5 "error:"
|
||||||
|
```
|
||||||
@@ -0,0 +1,180 @@
|
|||||||
|
---
|
||||||
|
name: fix-errors
|
||||||
|
description: Fix compilation errors, linting issues, and test failures in the warp Rust codebase. Covers presubmit checks, WASM-specific errors, and running specific tests. Use when the user hits build errors, clippy or fmt failures, test failures, or needs to run or interpret presubmit before a PR.
|
||||||
|
---
|
||||||
|
|
||||||
|
# fix-errors
|
||||||
|
|
||||||
|
Fix compilation errors, linting issues, and test failures in the warp Rust codebase.
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
This skill helps resolve common issues encountered during development, including:
|
||||||
|
- Compilation errors (unused imports, type mismatches, etc.)
|
||||||
|
- Linting failures (clippy warnings)
|
||||||
|
- Formatting violations
|
||||||
|
- WASM-specific errors
|
||||||
|
- Test failures
|
||||||
|
|
||||||
|
Before opening or updating a pull request, all presubmit checks must pass.
|
||||||
|
|
||||||
|
## Presubmit Checks
|
||||||
|
|
||||||
|
Run all presubmit checks at once:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./script/presubmit
|
||||||
|
```
|
||||||
|
|
||||||
|
This runs formatting, linting, and all tests. If it passes, you're ready to open a PR.
|
||||||
|
|
||||||
|
### Individual Checks
|
||||||
|
|
||||||
|
Run checks separately when debugging specific issues:
|
||||||
|
|
||||||
|
**Rust formatting:**
|
||||||
|
```bash
|
||||||
|
cargo fmt -- --check
|
||||||
|
```
|
||||||
|
|
||||||
|
**Clippy (full workspace):**
|
||||||
|
```bash
|
||||||
|
cargo clippy --workspace --exclude warp_completer --all-targets --all-features --tests -- -D warnings
|
||||||
|
cargo clippy -p warp_completer --all-targets --tests -- -D warnings
|
||||||
|
```
|
||||||
|
|
||||||
|
**WASM Clippy:**
|
||||||
|
```bash
|
||||||
|
cargo clippy --target wasm32-unknown-unknown --profile release-wasm-debug_assertions --no-deps
|
||||||
|
```
|
||||||
|
|
||||||
|
**Objective-C/C/C++ formatting:**
|
||||||
|
```bash
|
||||||
|
./script/run-clang-format.py -r --extensions 'c,h,cpp,m' ./crates/warpui/src/ ./app/src/
|
||||||
|
```
|
||||||
|
|
||||||
|
**All tests:**
|
||||||
|
```bash
|
||||||
|
cargo nextest run --no-fail-fast --workspace --exclude command-signatures-v2
|
||||||
|
cargo nextest run -p warp_completer --features v2
|
||||||
|
```
|
||||||
|
|
||||||
|
**Doc tests:**
|
||||||
|
```bash
|
||||||
|
cargo test --doc
|
||||||
|
```
|
||||||
|
|
||||||
|
## Running Specific Tests
|
||||||
|
|
||||||
|
**Single package:**
|
||||||
|
```bash
|
||||||
|
cargo nextest run -p <package_name>
|
||||||
|
```
|
||||||
|
|
||||||
|
**Filter by test name:**
|
||||||
|
```bash
|
||||||
|
cargo nextest run -E 'test(<substring>)'
|
||||||
|
```
|
||||||
|
|
||||||
|
**Specific package with filter:**
|
||||||
|
```bash
|
||||||
|
cargo nextest run -p <package_name> -E 'test(<substring>)'
|
||||||
|
```
|
||||||
|
|
||||||
|
**With output (no capture):**
|
||||||
|
```bash
|
||||||
|
cargo nextest run -p <package> --nocapture
|
||||||
|
```
|
||||||
|
|
||||||
|
## Common Error Types
|
||||||
|
|
||||||
|
### Unused Imports
|
||||||
|
Remove unused `use` statements identified by the compiler.
|
||||||
|
|
||||||
|
### Unused Constants
|
||||||
|
Remove constants that are defined but never used.
|
||||||
|
|
||||||
|
### Unknown Imports
|
||||||
|
Add the correct `use` statement for undefined types. Search the codebase to find the correct module path.
|
||||||
|
|
||||||
|
### Type Mismatches
|
||||||
|
Update function calls to pass arguments of the correct type. Common fixes:
|
||||||
|
- Use `.as_str()` instead of `.clone()` when a `&str` is expected
|
||||||
|
- Use `&value` when a reference is needed
|
||||||
|
- Use `.to_string()` when `String` is expected but `&str` is provided
|
||||||
|
|
||||||
|
### Struct Field Changes
|
||||||
|
When a struct adds/removes fields, update all places where it's constructed or destructured:
|
||||||
|
- Struct initialization
|
||||||
|
- Pattern matching (`match`, `if let`)
|
||||||
|
- Destructuring assignments
|
||||||
|
|
||||||
|
### Function Signature Changes
|
||||||
|
When a function adds a new parameter, update all call sites to provide the new argument:
|
||||||
|
- For `bool` params: pass `true` or `false` based on context
|
||||||
|
- For `Option<T>` params: pass `None` as default or `Some(value)` if needed
|
||||||
|
|
||||||
|
### Enum Variant Changes
|
||||||
|
When adding a new enum variant, update exhaustive `match` statements:
|
||||||
|
- Add a new match arm with appropriate handling
|
||||||
|
- Mirror the implementation pattern of similar variants
|
||||||
|
|
||||||
|
### Incorrect Trait Implementation
|
||||||
|
Fix trait implementations that return the wrong type or don't satisfy trait bounds.
|
||||||
|
|
||||||
|
### WASM-Specific Errors
|
||||||
|
|
||||||
|
WASM builds (`wasm32-unknown-unknown` target) don't support filesystem operations. Code that uses filesystem APIs must be gated behind the `local_fs` feature flag.
|
||||||
|
|
||||||
|
**Common WASM errors:**
|
||||||
|
- Dead code warnings for code only used in non-WASM builds
|
||||||
|
- Unused code that's only relevant when `local_fs` is available
|
||||||
|
- Tests that require filesystem access
|
||||||
|
|
||||||
|
**Fixes:**
|
||||||
|
|
||||||
|
**Gate tests behind `local_fs`:**
|
||||||
|
```rust
|
||||||
|
#[test]
|
||||||
|
#[cfg(feature = "local_fs")]
|
||||||
|
fn test_find_git_repo_with_worktree() {
|
||||||
|
// Test that uses filesystem operations
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Conditionally allow dead code for types only used when `local_fs` is enabled:**
|
||||||
|
```rust
|
||||||
|
#[cfg_attr(not(feature = "local_fs"), allow(dead_code))]
|
||||||
|
#[derive(Clone, EnumDiscriminants, Serialize)]
|
||||||
|
pub enum ExampleType {
|
||||||
|
// Variants only used when local_fs is enabled
|
||||||
|
Variant1,
|
||||||
|
Variant2,
|
||||||
|
Variant3,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
WASM errors are discovered by running:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo clippy --target wasm32-unknown-unknown --profile release-wasm-debug_assertions --no-deps
|
||||||
|
```
|
||||||
|
|
||||||
|
## Best Practices
|
||||||
|
|
||||||
|
**Before fixing:**
|
||||||
|
- Read the full error message to understand the root cause
|
||||||
|
- Check if multiple errors are related (fixing one may resolve others)
|
||||||
|
- For trait/type errors, verify you understand the expected vs actual types
|
||||||
|
- For WASM errors, check if code needs to be gated behind `local_fs`
|
||||||
|
|
||||||
|
**When fixing:**
|
||||||
|
- Fix one error type at a time when there are multiple issues
|
||||||
|
- Run `cargo check` frequently to verify fixes
|
||||||
|
- For WASM errors, run WASM clippy to verify the fix
|
||||||
|
- For complex changes, run relevant tests after fixing
|
||||||
|
|
||||||
|
**After fixing:**
|
||||||
|
- Always run `cargo fmt` and `cargo clippy` before pushing
|
||||||
|
- Run the full presubmit script before opening or updating a PR. Use the `create-pr` skill for more detailed instructions
|
||||||
|
- Verify tests pass in the areas you modified
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
---
|
||||||
|
name: implement-specs
|
||||||
|
description: Implement an approved feature from PRODUCT.md and TECH.md, keeping specs and code aligned in the same PR as implementation evolves. Use after the product and tech specs are approved and the next step is building the feature.
|
||||||
|
---
|
||||||
|
|
||||||
|
# implement-specs
|
||||||
|
|
||||||
|
Implement an approved feature from `PRODUCT.md` and `TECH.md`.
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
Use this skill after the product and tech specs are approved. The goal is to build the feature described by the specs while keeping the checked-in specs and the implementation aligned as the work evolves.
|
||||||
|
|
||||||
|
Approved specs should live directly under a ticket-named directory in `specs/`, for example `specs/APP-1234/PRODUCT.md` and `specs/APP-1234/TECH.md`.
|
||||||
|
|
||||||
|
In many cases, the implementation should be pushed in the same PR as the product and tech specs. As the engineer iterates, changes to `PRODUCT.md`, `TECH.md`, and the code should all be pushed in that same PR so review stays anchored to the feature that will actually ship.
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
Before using this skill:
|
||||||
|
|
||||||
|
- confirm that `PRODUCT.md` exists
|
||||||
|
- confirm that `TECH.md` exists when the feature warranted one
|
||||||
|
- confirm that the relevant specs have been reviewed and approved enough to start implementation
|
||||||
|
|
||||||
|
## Workflow
|
||||||
|
|
||||||
|
### 1. Read the approved specs first
|
||||||
|
|
||||||
|
Treat:
|
||||||
|
|
||||||
|
- `PRODUCT.md` as the source of truth for user-facing behavior
|
||||||
|
- `TECH.md` as the source of truth for architecture, sequencing, and implementation shape
|
||||||
|
|
||||||
|
Make sure you understand the expected behavior, constraints, risks, and validation plan before writing code.
|
||||||
|
|
||||||
|
### 2. Offer optional implementation aids for large features
|
||||||
|
|
||||||
|
For large or long-running features, optionally offer one of these aids to the user before implementation begins:
|
||||||
|
|
||||||
|
- `PROJECT_LOG.md` to track checkpoints, explored paths, partial findings, and current implementation state
|
||||||
|
- `DECISIONS.md` to capture concrete product and technical decisions made during the PRD and tech design process
|
||||||
|
|
||||||
|
These are optional aids, not required deliverables. Offer them when they would reduce confusion or help future agents avoid re-exploring the same paths.
|
||||||
|
|
||||||
|
### 3. Plan and implement against the specs
|
||||||
|
|
||||||
|
Break the work into concrete implementation steps, then implement the feature against the approved specs.
|
||||||
|
|
||||||
|
During implementation:
|
||||||
|
|
||||||
|
- keep behavior aligned with `PRODUCT.md`
|
||||||
|
- keep architecture and sequencing aligned with `TECH.md`
|
||||||
|
- add or update tests and verification artifacts as the work lands
|
||||||
|
|
||||||
|
Use the same PR for the specs and implementation when practical so the full feature evolution is reviewable in one place.
|
||||||
|
|
||||||
|
### 4. Update specs as the implementation evolves
|
||||||
|
|
||||||
|
If implementation reveals that the intended behavior or design should change, update the checked-in specs rather than letting them go stale.
|
||||||
|
|
||||||
|
In particular:
|
||||||
|
|
||||||
|
- update `PRODUCT.md` when user-facing behavior, UX, edge cases, or success criteria change
|
||||||
|
- update `TECH.md` when architecture, sequencing, module boundaries, or validation strategy change
|
||||||
|
- keep those updates in the same PR as the corresponding code changes
|
||||||
|
|
||||||
|
The PR should describe the feature that actually ships, not just the initial draft of the specs.
|
||||||
|
|
||||||
|
### 5. Verify against the specs
|
||||||
|
|
||||||
|
Before considering the work complete, verify that the code matches the current specs.
|
||||||
|
|
||||||
|
Prefer:
|
||||||
|
|
||||||
|
- `rust-unit-tests` for unit tests and regression coverage
|
||||||
|
- integration or end-to-end tests for important user flows
|
||||||
|
|
||||||
|
## Best Practices
|
||||||
|
|
||||||
|
- Keep specs and code synchronized throughout implementation.
|
||||||
|
- Prefer updating the spec immediately when decisions change rather than batching spec cleanup until the end.
|
||||||
|
- Use optional tracking documents only when they add real value for a complex feature.
|
||||||
|
- Keep the same PR coherent: spec updates, code changes, tests, and optional tracking docs should all support the same feature narrative.
|
||||||
|
|
||||||
|
## Related Skills
|
||||||
|
|
||||||
|
- `spec-driven-implementation`
|
||||||
|
- `write-product-spec`
|
||||||
|
- `write-tech-spec`
|
||||||
|
- `rust-unit-tests`
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
---
|
||||||
|
name: promote-feature
|
||||||
|
description: Promote a feature-flagged feature to Dogfood, Preview, or Stable in the Warp codebase. Use when a feature behind a FeatureFlag is ready to roll out to a broader audience, including wiring up the compile-time/runtime bridge and deferring flag cleanup safely.
|
||||||
|
---
|
||||||
|
|
||||||
|
# promote-feature
|
||||||
|
|
||||||
|
Guides the staged promotion of a gated `FeatureFlag` variant to Dogfood, Preview, or Stable, and schedules the follow-up cleanup.
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
Feature flags have two interacting layers:
|
||||||
|
- **Runtime** (`warp_core/src/features.rs`): `DOGFOOD_FLAGS`, `PREVIEW_FLAGS`, `RELEASE_FLAGS` — enabled per-channel at startup.
|
||||||
|
- **Compile-time** (`app/Cargo.toml` + `app/src/lib.rs`): Cargo features in `[features]`. The `default = [...]` array enables a feature for all builds. `enabled_features()` in `app/src/lib.rs` bridges each Cargo feature to its `FeatureFlag` variant via `#[cfg(feature = "...")]`.
|
||||||
|
|
||||||
|
**Do not remove the flag immediately after promoting to Stable.** Keep it for at least 1–2 release cycles so a rollback is a one-line PR (remove the entry from `default`). Use the `remove-feature-flag` skill for the cleanup step later.
|
||||||
|
|
||||||
|
## Promote to Dogfood
|
||||||
|
|
||||||
|
Add the flag to `DOGFOOD_FLAGS` in `warp_core/src/features.rs`:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
pub const DOGFOOD_FLAGS: &[FeatureFlag] = &[
|
||||||
|
// ...
|
||||||
|
FeatureFlag::YourFeature,
|
||||||
|
];
|
||||||
|
```
|
||||||
|
|
||||||
|
No other file changes needed.
|
||||||
|
|
||||||
|
## Promote to Preview
|
||||||
|
|
||||||
|
1. Add to `PREVIEW_FLAGS` in `warp_core/src/features.rs`.
|
||||||
|
2. Remove from `DOGFOOD_FLAGS` if present — Preview flags are automatically included in Dogfood builds.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
pub const PREVIEW_FLAGS: &[FeatureFlag] = &[
|
||||||
|
// ...
|
||||||
|
FeatureFlag::YourFeature,
|
||||||
|
];
|
||||||
|
```
|
||||||
|
|
||||||
|
## Promote to Stable
|
||||||
|
|
||||||
|
This requires changes in **three files**.
|
||||||
|
|
||||||
|
### 1. `app/Cargo.toml` — add to `default`
|
||||||
|
|
||||||
|
Add the snake_case feature name to the `default = [...]` array:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
default = [
|
||||||
|
# ...
|
||||||
|
"your_feature_name",
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
Prefer this over adding to `RELEASE_FLAGS` (see comment at `warp_core/src/features.rs:787-790`). It compiles the feature into all builds and enables a one-line rollback.
|
||||||
|
|
||||||
|
### 2. `app/src/lib.rs` — add to `enabled_features()` bridge
|
||||||
|
|
||||||
|
Add a `#[cfg(...)]` entry inside the `flags.extend([...])` block in `enabled_features()`, following the existing pattern:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
#[cfg(feature = "your_feature_name")]
|
||||||
|
FeatureFlag::YourFeature,
|
||||||
|
```
|
||||||
|
|
||||||
|
Place it near logically related entries.
|
||||||
|
|
||||||
|
### 3. `warp_core/src/features.rs` — remove from `PREVIEW_FLAGS` / `DOGFOOD_FLAGS`
|
||||||
|
|
||||||
|
Remove the variant from whichever arrays it currently lives in:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
pub const PREVIEW_FLAGS: &[FeatureFlag] = &[
|
||||||
|
// Remove FeatureFlag::YourFeature,
|
||||||
|
];
|
||||||
|
```
|
||||||
|
|
||||||
|
### Validate
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo fmt
|
||||||
|
cargo clippy --workspace --all-targets --all-features --tests -- -D warnings
|
||||||
|
```
|
||||||
|
|
||||||
|
### Create a follow-up Linear issue
|
||||||
|
|
||||||
|
After the PR lands, create a Linear issue to remind the team to remove the flag. Use the Linear MCP tool:
|
||||||
|
|
||||||
|
```
|
||||||
|
save_issue(
|
||||||
|
title: "Remove FeatureFlag::YourFeature after stabilization",
|
||||||
|
team: <your team>,
|
||||||
|
assignee: "me",
|
||||||
|
description: "FeatureFlag::YourFeature was promoted to Stable in <PR link>. Remove the flag and dead code branches after 1–2 release cycles. Follow the `remove-feature-flag` skill.",
|
||||||
|
labels: ["tech-debt"],
|
||||||
|
priority: 4 // Low
|
||||||
|
)
|
||||||
|
```
|
||||||
@@ -0,0 +1,148 @@
|
|||||||
|
---
|
||||||
|
name: remove-feature-flag
|
||||||
|
description: Remove a feature flag after it has been rolled out and stabilized in the Warp codebase.
|
||||||
|
---
|
||||||
|
|
||||||
|
# remove-feature-flag
|
||||||
|
|
||||||
|
Remove a feature flag after it has been rolled out and stabilized in the Warp codebase.
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
After a feature flag has been enabled for all users and has stabilized in production, the flag should be removed to reduce technical debt and simplify the codebase. This involves removing the flag definition and all conditional checks.
|
||||||
|
|
||||||
|
## When to Remove
|
||||||
|
|
||||||
|
Remove a feature flag when:
|
||||||
|
- The feature has been enabled in `default` features in `app/Cargo.toml`
|
||||||
|
- The feature has been stable in production for a reasonable period
|
||||||
|
- There are no plans to disable the feature or provide configuration options
|
||||||
|
- The team agrees the feature is permanent
|
||||||
|
|
||||||
|
## Steps
|
||||||
|
|
||||||
|
### 1. Remove from app/Cargo.toml
|
||||||
|
Remove the feature from both the `[features]` section and the `default` array:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[features]
|
||||||
|
default = [
|
||||||
|
# Remove "your_feature_name" from here
|
||||||
|
]
|
||||||
|
|
||||||
|
# Remove this line:
|
||||||
|
# your_feature_name = []
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Remove from FeatureFlag enum
|
||||||
|
Remove the variant from the `FeatureFlag` enum in `warp_core/src/features.rs`:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
#[derive(Sequence)]
|
||||||
|
pub enum FeatureFlag {
|
||||||
|
// Remove YourFeatureName,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Remove from app/src/lib.rs
|
||||||
|
Remove the conditional compilation directive:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
// Remove these lines:
|
||||||
|
// #[cfg(feature = "your_feature_name")]
|
||||||
|
// YourFeatureName,
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Remove from DOGFOOD_FLAGS/PREVIEW_FLAGS/RELEASE_FLAGS
|
||||||
|
If the flag was listed in any of these arrays in `features.rs`, remove it:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
pub const DOGFOOD_FLAGS: &[FeatureFlag] = &[
|
||||||
|
// Remove FeatureFlag::YourFeatureName,
|
||||||
|
];
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. Remove all runtime checks and dead code
|
||||||
|
Find and remove all `FeatureFlag::YourFeatureName.is_enabled()` checks throughout the codebase:
|
||||||
|
|
||||||
|
**Before:**
|
||||||
|
```rust
|
||||||
|
if FeatureFlag::YourFeatureName.is_enabled() {
|
||||||
|
// new behavior
|
||||||
|
} else {
|
||||||
|
// old behavior (dead code)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**After:**
|
||||||
|
```rust
|
||||||
|
// new behavior (unconditionally enabled)
|
||||||
|
```
|
||||||
|
|
||||||
|
Use ripgrep to find all occurrences:
|
||||||
|
```bash
|
||||||
|
rg "YourFeatureName" app/ warp_core/
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6. Remove keybinding predicates
|
||||||
|
If the feature flag was used in keybinding enabled predicates, remove the predicate:
|
||||||
|
|
||||||
|
**Before:**
|
||||||
|
```rust
|
||||||
|
EditableBinding::new(
|
||||||
|
"action:name",
|
||||||
|
"Action description",
|
||||||
|
YourAction::Variant
|
||||||
|
)
|
||||||
|
.with_enabled(|| FeatureFlag::YourFeatureName.is_enabled())
|
||||||
|
.with_key_binding("cmdorctrl-key")
|
||||||
|
```
|
||||||
|
|
||||||
|
**After:**
|
||||||
|
```rust
|
||||||
|
EditableBinding::new(
|
||||||
|
"action:name",
|
||||||
|
"Action description",
|
||||||
|
YourAction::Variant
|
||||||
|
)
|
||||||
|
.with_key_binding("cmdorctrl-key")
|
||||||
|
```
|
||||||
|
|
||||||
|
### 7. Clean up dead code branches
|
||||||
|
Remove any code paths that were only executed when the feature was disabled (the `else` branches in feature checks). These are now dead code.
|
||||||
|
|
||||||
|
### 8. Run tests and validation
|
||||||
|
After removing the flag:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Format and lint
|
||||||
|
cargo fmt
|
||||||
|
cargo clippy --workspace --all-targets --all-features --tests -- -D warnings
|
||||||
|
|
||||||
|
# Run tests
|
||||||
|
cargo nextest run --no-fail-fast --workspace --exclude command-signatures-v2
|
||||||
|
|
||||||
|
# Build the app
|
||||||
|
cargo run
|
||||||
|
```
|
||||||
|
|
||||||
|
## Best Practices
|
||||||
|
|
||||||
|
- Remove feature flags promptly after they're no longer needed to reduce technical debt
|
||||||
|
- When removing a flag, remove ALL related code (checks, dead branches, keybinding predicates)
|
||||||
|
- Use grep/ripgrep to ensure you've found all occurrences
|
||||||
|
- Test thoroughly after removal to ensure no regressions
|
||||||
|
- Consider doing flag removal in a separate PR for easier review
|
||||||
|
|
||||||
|
## Example Search Commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Find all occurrences of the flag name
|
||||||
|
rg "YourFeatureName" app/ warp_core/
|
||||||
|
|
||||||
|
# Find feature flag checks
|
||||||
|
rg "FeatureFlag::YourFeatureName" app/
|
||||||
|
|
||||||
|
# Find cfg attributes
|
||||||
|
rg 'cfg\(feature = "your_feature_name"\)' app/
|
||||||
|
```
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
---
|
||||||
|
name: resolve-merge-conflicts
|
||||||
|
description: Resolve Git merge conflicts by extracting only unresolved paths, conflict hunks, and compact diffs instead of loading whole files into context. Use when a merge, rebase, cherry-pick, or stash pop stops on conflicts, when `git status` shows unmerged paths, or when files contain conflict markers.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Resolve Merge Conflicts
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
Resolve conflicts without opening full files unless the compact view is insufficient. Start with a summary, then inspect one conflicted file at a time.
|
||||||
|
|
||||||
|
## Workflow
|
||||||
|
|
||||||
|
1. Start with a summary.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 .agents/skills/resolve-merge-conflicts/scripts/extract_conflict_context.py
|
||||||
|
```
|
||||||
|
|
||||||
|
Use the summary to identify which files are unresolved, which index stages exist, and how many text hunks each file contains.
|
||||||
|
|
||||||
|
2. Drill into one file.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 .agents/skills/resolve-merge-conflicts/scripts/extract_conflict_context.py --file path/to/file
|
||||||
|
```
|
||||||
|
|
||||||
|
Prefer this over reading the whole file. The script prints only nearby context, the `ours` / `base` / `theirs` sections for each hunk, and a compact unified diff between `ours` and `theirs`.
|
||||||
|
|
||||||
|
3. Resolve the file.
|
||||||
|
|
||||||
|
- Take one side wholesale with `git checkout --ours -- path/to/file` or `git checkout --theirs -- path/to/file` when appropriate.
|
||||||
|
- Otherwise edit the file directly and remove the conflict markers.
|
||||||
|
- Read more of the file only if the compact output is not enough to decide the correct merge.
|
||||||
|
|
||||||
|
4. Re-check unresolved files.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 .agents/skills/resolve-merge-conflicts/scripts/extract_conflict_context.py
|
||||||
|
git diff --name-only --diff-filter=U
|
||||||
|
```
|
||||||
|
|
||||||
|
5. Validate the resolution.
|
||||||
|
|
||||||
|
- Ensure no unmerged paths remain.
|
||||||
|
- Ensure no `<<<<<<<`, `=======`, or `>>>>>>>` markers remain in the resolved files.
|
||||||
|
- Run targeted tests, builds, or linters for the touched area.
|
||||||
|
- Stage the resolved files.
|
||||||
|
|
||||||
|
## Commands
|
||||||
|
|
||||||
|
### Summary only
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 .agents/skills/resolve-merge-conflicts/scripts/extract_conflict_context.py
|
||||||
|
```
|
||||||
|
|
||||||
|
### Detailed view for one file
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 .agents/skills/resolve-merge-conflicts/scripts/extract_conflict_context.py --file path/to/file
|
||||||
|
```
|
||||||
|
|
||||||
|
### Detailed view for all conflicted files
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 .agents/skills/resolve-merge-conflicts/scripts/extract_conflict_context.py --all
|
||||||
|
```
|
||||||
|
|
||||||
|
### JSON output
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 .agents/skills/resolve-merge-conflicts/scripts/extract_conflict_context.py --file path/to/file --json
|
||||||
|
```
|
||||||
|
|
||||||
|
### Tune output size
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 .agents/skills/resolve-merge-conflicts/scripts/extract_conflict_context.py \
|
||||||
|
--file path/to/file \
|
||||||
|
--context 3 \
|
||||||
|
--max-lines 60
|
||||||
|
```
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- Use the script before opening conflicted files directly.
|
||||||
|
- Resolve one file at a time to keep context small.
|
||||||
|
- Expect marker-based text conflicts and index-only conflicts such as add/add or modify/delete. The script summarizes both, and it falls back to index-stage previews when the worktree file has no conflict markers.
|
||||||
+468
@@ -0,0 +1,468 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Summarize and extract compact merge-conflict context from a Git repository."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import difflib
|
||||||
|
import json
|
||||||
|
import re
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
START_RE = re.compile(r"^<<<<<<<(?: (.*))?$")
|
||||||
|
BASE_RE = re.compile(r"^\|\|\|\|\|\|\|(?: (.*))?$")
|
||||||
|
END_RE = re.compile(r"^>>>>>>>(?: (.*))?$")
|
||||||
|
|
||||||
|
|
||||||
|
def run_git(repo_root: Path, *args: str) -> str:
|
||||||
|
result = subprocess.run(
|
||||||
|
["git", "-C", str(repo_root), *args],
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
check=False,
|
||||||
|
)
|
||||||
|
if result.returncode != 0:
|
||||||
|
message = result.stderr.strip() or result.stdout.strip() or "unknown git error"
|
||||||
|
raise RuntimeError(f"git {' '.join(args)} failed: {message}")
|
||||||
|
return result.stdout
|
||||||
|
|
||||||
|
|
||||||
|
def find_repo_root(start: Path) -> Path:
|
||||||
|
result = subprocess.run(
|
||||||
|
["git", "-C", str(start), "rev-parse", "--show-toplevel"],
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
check=False,
|
||||||
|
)
|
||||||
|
if result.returncode != 0:
|
||||||
|
message = result.stderr.strip() or result.stdout.strip() or "not a git repository"
|
||||||
|
raise RuntimeError(message)
|
||||||
|
return Path(result.stdout.strip()).resolve()
|
||||||
|
|
||||||
|
|
||||||
|
def get_unmerged_entries(repo_root: Path) -> dict[str, dict[int, dict[str, str]]]:
|
||||||
|
entries: dict[str, dict[int, dict[str, str]]] = {}
|
||||||
|
output = run_git(repo_root, "ls-files", "-u", "-z")
|
||||||
|
for record in output.split("\0"):
|
||||||
|
if not record:
|
||||||
|
continue
|
||||||
|
metadata, path = record.split("\t", 1)
|
||||||
|
mode, object_id, stage_text = metadata.split()
|
||||||
|
file_entry = entries.setdefault(path, {})
|
||||||
|
file_entry[int(stage_text)] = {"mode": mode, "object_id": object_id}
|
||||||
|
return entries
|
||||||
|
|
||||||
|
|
||||||
|
def read_text_file(path: Path) -> list[str] | None:
|
||||||
|
if not path.exists() or path.is_dir():
|
||||||
|
return None
|
||||||
|
try:
|
||||||
|
text = path.read_text(encoding="utf-8", errors="replace")
|
||||||
|
except OSError:
|
||||||
|
return None
|
||||||
|
if "\x00" in text:
|
||||||
|
return None
|
||||||
|
return text.splitlines()
|
||||||
|
|
||||||
|
|
||||||
|
def read_stage_text(repo_root: Path, path: str, stage: int) -> list[str] | None:
|
||||||
|
result = subprocess.run(
|
||||||
|
["git", "-C", str(repo_root), "show", f":{stage}:{path}"],
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
check=False,
|
||||||
|
)
|
||||||
|
if result.returncode != 0:
|
||||||
|
return None
|
||||||
|
if "\x00" in result.stdout:
|
||||||
|
return None
|
||||||
|
return result.stdout.splitlines()
|
||||||
|
|
||||||
|
|
||||||
|
def truncate_lines(lines: list[str], max_lines: int) -> list[str]:
|
||||||
|
if len(lines) <= max_lines:
|
||||||
|
return lines
|
||||||
|
omitted = len(lines) - max_lines
|
||||||
|
return [*lines[:max_lines], f"... ({omitted} more lines omitted)"]
|
||||||
|
|
||||||
|
|
||||||
|
def build_diff(
|
||||||
|
left_lines: list[str],
|
||||||
|
right_lines: list[str],
|
||||||
|
left_label: str,
|
||||||
|
right_label: str,
|
||||||
|
max_lines: int,
|
||||||
|
) -> list[str]:
|
||||||
|
diff = list(
|
||||||
|
difflib.unified_diff(
|
||||||
|
left_lines,
|
||||||
|
right_lines,
|
||||||
|
fromfile=left_label,
|
||||||
|
tofile=right_label,
|
||||||
|
lineterm="",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if not diff:
|
||||||
|
diff = ["(no textual diff)"]
|
||||||
|
return truncate_lines(diff, max_lines)
|
||||||
|
|
||||||
|
|
||||||
|
def classify_conflict(stages: list[int], marker_hunks: int) -> str:
|
||||||
|
if marker_hunks:
|
||||||
|
return "text"
|
||||||
|
stage_set = set(stages)
|
||||||
|
if stage_set == {2, 3}:
|
||||||
|
return "add/add"
|
||||||
|
if stage_set == {1, 2}:
|
||||||
|
return "deleted-by-them"
|
||||||
|
if stage_set == {1, 3}:
|
||||||
|
return "deleted-by-us"
|
||||||
|
if stage_set == {1, 2, 3}:
|
||||||
|
return "index-only"
|
||||||
|
return "unmerged"
|
||||||
|
|
||||||
|
|
||||||
|
def normalize_requested_path(repo_root: Path, raw_path: str) -> str:
|
||||||
|
path = Path(raw_path)
|
||||||
|
candidate = path.resolve() if path.is_absolute() else (repo_root / path).resolve()
|
||||||
|
try:
|
||||||
|
return str(candidate.relative_to(repo_root))
|
||||||
|
except ValueError as error:
|
||||||
|
raise RuntimeError(f"path is outside repository: {raw_path}") from error
|
||||||
|
|
||||||
|
|
||||||
|
def parse_conflict_hunks(lines: list[str], context: int) -> tuple[list[dict[str, object]], str | None]:
|
||||||
|
hunks: list[dict[str, object]] = []
|
||||||
|
index = 0
|
||||||
|
while index < len(lines):
|
||||||
|
start_match = START_RE.match(lines[index])
|
||||||
|
if not start_match:
|
||||||
|
index += 1
|
||||||
|
continue
|
||||||
|
|
||||||
|
start_index = index
|
||||||
|
ours_label = start_match.group(1) or "ours"
|
||||||
|
index += 1
|
||||||
|
ours: list[str] = []
|
||||||
|
base: list[str] = []
|
||||||
|
theirs: list[str] = []
|
||||||
|
base_label: str | None = None
|
||||||
|
theirs_label = "theirs"
|
||||||
|
|
||||||
|
while index < len(lines):
|
||||||
|
base_match = BASE_RE.match(lines[index])
|
||||||
|
if base_match:
|
||||||
|
base_label = base_match.group(1) or "base"
|
||||||
|
index += 1
|
||||||
|
while index < len(lines) and lines[index] != "=======":
|
||||||
|
base.append(lines[index])
|
||||||
|
index += 1
|
||||||
|
break
|
||||||
|
if lines[index] == "=======":
|
||||||
|
break
|
||||||
|
ours.append(lines[index])
|
||||||
|
index += 1
|
||||||
|
|
||||||
|
if index >= len(lines) or lines[index] != "=======":
|
||||||
|
return hunks, f"unterminated conflict starting at line {start_index + 1}"
|
||||||
|
|
||||||
|
index += 1
|
||||||
|
end_index = index
|
||||||
|
while index < len(lines):
|
||||||
|
end_match = END_RE.match(lines[index])
|
||||||
|
if end_match:
|
||||||
|
theirs_label = end_match.group(1) or "theirs"
|
||||||
|
end_index = index
|
||||||
|
index += 1
|
||||||
|
break
|
||||||
|
theirs.append(lines[index])
|
||||||
|
index += 1
|
||||||
|
else:
|
||||||
|
return hunks, f"unterminated conflict starting at line {start_index + 1}"
|
||||||
|
|
||||||
|
hunks.append(
|
||||||
|
{
|
||||||
|
"start_line": start_index + 1,
|
||||||
|
"end_line": end_index + 1,
|
||||||
|
"before_context": lines[max(0, start_index - context):start_index],
|
||||||
|
"ours": ours,
|
||||||
|
"ours_label": ours_label,
|
||||||
|
"base": base or None,
|
||||||
|
"base_label": base_label,
|
||||||
|
"theirs": theirs,
|
||||||
|
"theirs_label": theirs_label,
|
||||||
|
"after_context": lines[index:index + context],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
return hunks, None
|
||||||
|
|
||||||
|
|
||||||
|
def build_summary_report(repo_root: Path, path: str, stage_entries: dict[int, dict[str, str]], context: int) -> dict[str, object]:
|
||||||
|
worktree_lines = read_text_file(repo_root / path)
|
||||||
|
hunks: list[dict[str, object]] = []
|
||||||
|
parse_error = None
|
||||||
|
if worktree_lines is not None:
|
||||||
|
hunks, parse_error = parse_conflict_hunks(worktree_lines, context)
|
||||||
|
stages = sorted(stage_entries)
|
||||||
|
return {
|
||||||
|
"path": path,
|
||||||
|
"stages": stages,
|
||||||
|
"conflict_type": classify_conflict(stages, len(hunks)),
|
||||||
|
"marker_hunks": len(hunks),
|
||||||
|
"parse_error": parse_error,
|
||||||
|
"worktree_present": worktree_lines is not None,
|
||||||
|
"hunks": hunks,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def build_index_preview(repo_root: Path, report: dict[str, object], max_lines: int) -> dict[str, object]:
|
||||||
|
path = str(report["path"])
|
||||||
|
ours = read_stage_text(repo_root, path, 2)
|
||||||
|
theirs = read_stage_text(repo_root, path, 3)
|
||||||
|
base = read_stage_text(repo_root, path, 1)
|
||||||
|
preview: dict[str, object] = {
|
||||||
|
"ours": truncate_lines(ours, max_lines) if ours else None,
|
||||||
|
"theirs": truncate_lines(theirs, max_lines) if theirs else None,
|
||||||
|
"base": truncate_lines(base, max_lines) if base else None,
|
||||||
|
}
|
||||||
|
if ours and theirs:
|
||||||
|
preview["ours_vs_theirs_diff"] = build_diff(ours, theirs, "ours", "theirs", max_lines)
|
||||||
|
return preview
|
||||||
|
|
||||||
|
|
||||||
|
def section_lines(title: str, lines: list[str] | None) -> list[str]:
|
||||||
|
if lines is None:
|
||||||
|
return [f"{title}:", " (not present)"]
|
||||||
|
if not lines:
|
||||||
|
return [f"{title}:", " (empty)"]
|
||||||
|
return [f"{title}:", *[f" {line}" for line in lines]]
|
||||||
|
|
||||||
|
|
||||||
|
def render_summary_text(repo_root: Path, reports: list[dict[str, object]]) -> str:
|
||||||
|
lines = [f"repo: {repo_root}", f"conflicted files: {len(reports)}"]
|
||||||
|
for report in reports:
|
||||||
|
stages = ",".join(str(stage) for stage in report["stages"])
|
||||||
|
lines.append(
|
||||||
|
f"- {report['path']} | type={report['conflict_type']} | stages={stages} | hunks={report['marker_hunks']}"
|
||||||
|
)
|
||||||
|
if report["parse_error"]:
|
||||||
|
lines.append(f" parse-error: {report['parse_error']}")
|
||||||
|
lines.append("use --file <path> for compact hunk details or --all for every file")
|
||||||
|
return "\n".join(lines)
|
||||||
|
|
||||||
|
|
||||||
|
def render_detail_text(
|
||||||
|
repo_root: Path,
|
||||||
|
report: dict[str, object],
|
||||||
|
max_lines: int,
|
||||||
|
) -> str:
|
||||||
|
lines = [
|
||||||
|
f"== {report['path']} ==",
|
||||||
|
f"type: {report['conflict_type']}",
|
||||||
|
f"stages: {', '.join(str(stage) for stage in report['stages'])}",
|
||||||
|
]
|
||||||
|
parse_error = report["parse_error"]
|
||||||
|
if parse_error:
|
||||||
|
lines.append(f"parse-error: {parse_error}")
|
||||||
|
|
||||||
|
hunks = report["hunks"]
|
||||||
|
if hunks:
|
||||||
|
lines.append(f"hunks: {len(hunks)}")
|
||||||
|
for index, hunk in enumerate(hunks, start=1):
|
||||||
|
ours = list(hunk["ours"])
|
||||||
|
theirs = list(hunk["theirs"])
|
||||||
|
diff = build_diff(
|
||||||
|
ours,
|
||||||
|
theirs,
|
||||||
|
str(hunk["ours_label"]),
|
||||||
|
str(hunk["theirs_label"]),
|
||||||
|
max_lines,
|
||||||
|
)
|
||||||
|
lines.extend(
|
||||||
|
[
|
||||||
|
"",
|
||||||
|
f"[hunk {index}] current lines {hunk['start_line']}-{hunk['end_line']}",
|
||||||
|
*section_lines("before", truncate_lines(list(hunk["before_context"]), max_lines)),
|
||||||
|
*section_lines(
|
||||||
|
f"ours ({hunk['ours_label']})",
|
||||||
|
truncate_lines(ours, max_lines),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
if hunk["base"] is not None:
|
||||||
|
lines.extend(
|
||||||
|
section_lines(
|
||||||
|
f"base ({hunk['base_label'] or 'base'})",
|
||||||
|
truncate_lines(list(hunk["base"]), max_lines),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
lines.extend(
|
||||||
|
[
|
||||||
|
*section_lines(
|
||||||
|
f"theirs ({hunk['theirs_label']})",
|
||||||
|
truncate_lines(theirs, max_lines),
|
||||||
|
),
|
||||||
|
*section_lines("ours vs theirs diff", diff),
|
||||||
|
*section_lines("after", truncate_lines(list(hunk["after_context"]), max_lines)),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
return "\n".join(lines)
|
||||||
|
|
||||||
|
preview = build_index_preview(repo_root, report, max_lines)
|
||||||
|
lines.append("hunks: 0")
|
||||||
|
lines.append("index preview:")
|
||||||
|
lines.extend(section_lines("ours", preview["ours"]))
|
||||||
|
lines.extend(section_lines("base", preview["base"]))
|
||||||
|
lines.extend(section_lines("theirs", preview["theirs"]))
|
||||||
|
if "ours_vs_theirs_diff" in preview:
|
||||||
|
lines.extend(section_lines("ours vs theirs diff", preview["ours_vs_theirs_diff"]))
|
||||||
|
return "\n".join(lines)
|
||||||
|
|
||||||
|
|
||||||
|
def render_json(
|
||||||
|
repo_root: Path,
|
||||||
|
reports: list[dict[str, object]],
|
||||||
|
include_details: bool,
|
||||||
|
max_lines: int,
|
||||||
|
) -> str:
|
||||||
|
files: list[dict[str, object]] = []
|
||||||
|
for report in reports:
|
||||||
|
file_entry: dict[str, object] = {
|
||||||
|
"path": report["path"],
|
||||||
|
"conflict_type": report["conflict_type"],
|
||||||
|
"stages": report["stages"],
|
||||||
|
"marker_hunks": report["marker_hunks"],
|
||||||
|
"parse_error": report["parse_error"],
|
||||||
|
}
|
||||||
|
if include_details:
|
||||||
|
if report["hunks"]:
|
||||||
|
file_entry["hunks"] = [
|
||||||
|
{
|
||||||
|
"start_line": hunk["start_line"],
|
||||||
|
"end_line": hunk["end_line"],
|
||||||
|
"before_context": truncate_lines(list(hunk["before_context"]), max_lines),
|
||||||
|
"ours_label": hunk["ours_label"],
|
||||||
|
"ours": truncate_lines(list(hunk["ours"]), max_lines),
|
||||||
|
"base_label": hunk["base_label"],
|
||||||
|
"base": truncate_lines(list(hunk["base"]), max_lines) if hunk["base"] else None,
|
||||||
|
"theirs_label": hunk["theirs_label"],
|
||||||
|
"theirs": truncate_lines(list(hunk["theirs"]), max_lines),
|
||||||
|
"after_context": truncate_lines(list(hunk["after_context"]), max_lines),
|
||||||
|
"ours_vs_theirs_diff": build_diff(
|
||||||
|
list(hunk["ours"]),
|
||||||
|
list(hunk["theirs"]),
|
||||||
|
str(hunk["ours_label"]),
|
||||||
|
str(hunk["theirs_label"]),
|
||||||
|
max_lines,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
for hunk in report["hunks"]
|
||||||
|
]
|
||||||
|
else:
|
||||||
|
file_entry["index_preview"] = build_index_preview(repo_root, report, max_lines)
|
||||||
|
files.append(file_entry)
|
||||||
|
|
||||||
|
return json.dumps(
|
||||||
|
{
|
||||||
|
"repo_root": str(repo_root),
|
||||||
|
"conflicted_files": files,
|
||||||
|
},
|
||||||
|
indent=2,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description="Summarize and extract compact merge-conflict context."
|
||||||
|
)
|
||||||
|
parser.add_argument("--repo", default=".", help="Path inside the target repository.")
|
||||||
|
parser.add_argument(
|
||||||
|
"--file",
|
||||||
|
action="append",
|
||||||
|
default=[],
|
||||||
|
help="Conflicted file to inspect in detail. Repeat to inspect multiple files.",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--all",
|
||||||
|
action="store_true",
|
||||||
|
help="Print detailed output for every conflicted file.",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--json",
|
||||||
|
action="store_true",
|
||||||
|
help="Emit JSON instead of text.",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--context",
|
||||||
|
type=int,
|
||||||
|
default=2,
|
||||||
|
help="Lines of surrounding context to include around each conflict hunk.",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--max-lines",
|
||||||
|
type=int,
|
||||||
|
default=40,
|
||||||
|
help="Maximum lines to print for each section before truncating.",
|
||||||
|
)
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
if args.all and args.file:
|
||||||
|
parser.error("--all cannot be combined with --file")
|
||||||
|
if args.context < 0:
|
||||||
|
parser.error("--context must be non-negative")
|
||||||
|
if args.max_lines <= 0:
|
||||||
|
parser.error("--max-lines must be positive")
|
||||||
|
|
||||||
|
try:
|
||||||
|
repo_root = find_repo_root(Path(args.repo).resolve())
|
||||||
|
entries = get_unmerged_entries(repo_root)
|
||||||
|
except RuntimeError as error:
|
||||||
|
print(f"error: {error}", file=sys.stderr)
|
||||||
|
return 2
|
||||||
|
|
||||||
|
reports = [
|
||||||
|
build_summary_report(repo_root, path, entries[path], args.context)
|
||||||
|
for path in sorted(entries)
|
||||||
|
]
|
||||||
|
|
||||||
|
if not reports:
|
||||||
|
message = json.dumps({"repo_root": str(repo_root), "conflicted_files": []}, indent=2) if args.json else f"repo: {repo_root}\nconflicted files: 0"
|
||||||
|
print(message)
|
||||||
|
return 0
|
||||||
|
|
||||||
|
if args.all:
|
||||||
|
selected_reports = reports
|
||||||
|
elif args.file:
|
||||||
|
try:
|
||||||
|
requested_paths = {normalize_requested_path(repo_root, path) for path in args.file}
|
||||||
|
except RuntimeError as error:
|
||||||
|
print(f"error: {error}", file=sys.stderr)
|
||||||
|
return 2
|
||||||
|
known_paths = {str(report["path"]) for report in reports}
|
||||||
|
missing = sorted(requested_paths - known_paths)
|
||||||
|
if missing:
|
||||||
|
for path in missing:
|
||||||
|
print(f"error: conflicted file not found: {path}", file=sys.stderr)
|
||||||
|
return 2
|
||||||
|
selected_reports = [report for report in reports if report["path"] in requested_paths]
|
||||||
|
else:
|
||||||
|
selected_reports = []
|
||||||
|
|
||||||
|
if args.json:
|
||||||
|
print(render_json(repo_root, selected_reports or reports, bool(selected_reports), args.max_lines))
|
||||||
|
return 0
|
||||||
|
|
||||||
|
if not selected_reports:
|
||||||
|
print(render_summary_text(repo_root, reports))
|
||||||
|
return 0
|
||||||
|
|
||||||
|
print("\n\n".join(render_detail_text(repo_root, report, args.max_lines) for report in selected_reports))
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
raise SystemExit(main())
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
---
|
||||||
|
name: review-pr-local
|
||||||
|
specializes: review-pr
|
||||||
|
description: Repo-specific review guidance for warp-external. Only the categories declared overridable by the core review-pr skill may be specialized here.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Repo-specific review guidance for `warp-external`
|
||||||
|
|
||||||
|
This file is a companion to the core `review-pr` skill. It does not
|
||||||
|
redefine the review output schema, severity labels, safety rules, or
|
||||||
|
evidence rules. It only specializes the override categories the core
|
||||||
|
skill marks as overridable.
|
||||||
|
|
||||||
|
## Repo-specific style and recurring review patterns
|
||||||
|
|
||||||
|
- Do not suggest adding test cases that only vary constructor inputs or struct fields when an existing test already covers the meaningful behavior. Only suggest new tests when they exercise a distinct code path or edge case.
|
||||||
|
- When a PR is clearly a V0 or initial implementation, frame robustness suggestions such as timeouts, retries, and lifecycle management as optional future work rather than blocking concerns, unless they risk correctness, security, data loss, or a persistent UI hang.
|
||||||
|
- For Rust changes, apply the repository conventions from `WARP.md`: avoid unnecessary type annotations, prefer imports over long path qualifiers, name context parameters `ctx` and place them last, remove unused parameters instead of prefixing them with `_`, and prefer inline format arguments in macros.
|
||||||
|
- Avoid wildcard `_` match arms when an enum can reasonably be matched exhaustively; exhaustive matches are preferred so future variants are surfaced during review.
|
||||||
|
- For new or changed feature flags, prefer high-level runtime checks with `FeatureFlag::YourFlag.is_enabled()` over `#[cfg(...)]` unless the code cannot compile without a compile-time gate.
|
||||||
|
- Flag nested or redundant `TerminalModel` locking when the call stack may already hold the model lock. Prefer passing locked references down the stack and keeping lock scopes short.
|
||||||
|
- In WarpUI code, flag inline `MouseStateHandle::default()` usage during render or event handling. Mouse state handles should be created during construction and then cloned/referenced where needed.
|
||||||
|
- For user-facing UI changes, mention missing validation only when it is tied to a concrete risk or when the PR changes behavior that should be verified visually.
|
||||||
|
|
||||||
|
## User-facing strings
|
||||||
|
|
||||||
|
- Flag interpolated text that would read unnaturally at runtime or combine sentence fragments with the wrong casing.
|
||||||
|
- Link text should be descriptive rather than bare URLs or generic "click here" labels.
|
||||||
|
- Verify that product terminology is consistent across related UI, comments, workflow messages, and errors in the same PR.
|
||||||
|
|
||||||
|
## Graceful degradation and observability
|
||||||
|
|
||||||
|
- When optional dynamic data such as URLs, session links, workflow links, issue numbers, or metadata may be absent, prefer omitting the element or showing a short fallback over rendering empty or broken output.
|
||||||
|
- Do not suggest removing session links, workflow URLs, or diagnostic context from error paths. Those links are important for debugging failed automation and user reports.
|
||||||
|
- Prefer generic, user-safe error text in user-visible surfaces, but keep enough structured logging or diagnostic context for maintainers to investigate failures.
|
||||||
@@ -0,0 +1,109 @@
|
|||||||
|
---
|
||||||
|
name: review-pr
|
||||||
|
description: Review a pull request diff and write structured feedback to review.json for the workflow to publish. Use when reviewing a checked-out PR from local artifacts like pr_diff.txt and pr_description.txt and producing machine-readable review output instead of posting directly to GitHub.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Review PR Skill
|
||||||
|
|
||||||
|
Review the current pull request and write the output to `review.json`.
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
- The working directory is the PR branch checkout.
|
||||||
|
- The workflow provides an annotated diff in `pr_diff.txt`.
|
||||||
|
- The workflow provides the PR description in `pr_description.txt`.
|
||||||
|
- Focus on files and lines changed by this PR.
|
||||||
|
- Do not post comments or reviews to GitHub directly.
|
||||||
|
|
||||||
|
## Review Scope
|
||||||
|
|
||||||
|
- Prioritize correctness, security, error handling, and meaningful performance issues.
|
||||||
|
- Include style or nit comments only when you can provide a concrete suggestion block.
|
||||||
|
- If a concern involves untouched code, mention it in the summary instead of an inline comment.
|
||||||
|
- Do not suggest adding test cases that only vary constructor inputs or struct fields when the existing test already covers the meaningful behavior. Only suggest new tests when they exercise a distinct code path or edge case.
|
||||||
|
- When a PR is clearly a V0 or initial implementation, frame robustness suggestions (timeouts, retries, lifecycle management) as optional future work rather than blocking concerns, unless they risk correctness, security, or data loss.
|
||||||
|
|
||||||
|
## Diff Line Annotations
|
||||||
|
|
||||||
|
The diff file uses these prefixes:
|
||||||
|
|
||||||
|
- `[OLD:n]` for deleted lines on the old side. Use `"LEFT"`.
|
||||||
|
- `[NEW:n]` for added lines on the new side. Use `"RIGHT"`.
|
||||||
|
- `[OLD:n,NEW:m]` for unchanged context. Use `"RIGHT"` with line `m`.
|
||||||
|
|
||||||
|
## Comment Requirements
|
||||||
|
|
||||||
|
Every comment body must start with one of these labels:
|
||||||
|
|
||||||
|
- `🚨 [CRITICAL]` for bugs, security issues, crashes, or data loss.
|
||||||
|
- `⚠️ [IMPORTANT]` for logic problems, edge cases, or missing error handling.
|
||||||
|
- `💡 [SUGGESTION]` for worthwhile improvements or better patterns.
|
||||||
|
- `🧹 [NIT]` for cleanup only when the comment includes a suggestion block.
|
||||||
|
|
||||||
|
Write comments with these constraints:
|
||||||
|
|
||||||
|
- Be concise, direct, and actionable.
|
||||||
|
- Do not add compliments or hedging.
|
||||||
|
- Prefer single-line comments.
|
||||||
|
- Keep ranges to at most 10 lines.
|
||||||
|
- Restrict inline comments to valid changed lines in this PR.
|
||||||
|
|
||||||
|
## Suggestion Blocks
|
||||||
|
|
||||||
|
When proposing a code change, use:
|
||||||
|
|
||||||
|
```suggestion
|
||||||
|
<replacement code here>
|
||||||
|
```
|
||||||
|
|
||||||
|
Rules:
|
||||||
|
|
||||||
|
- Match the exact indentation of the original file.
|
||||||
|
- Include only replacement code.
|
||||||
|
- For multi-line suggestions, set `start_line` to the first line and `line` to the last line.
|
||||||
|
|
||||||
|
## Output Format
|
||||||
|
|
||||||
|
Create `review.json` with this shape:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"summary": "## Overview\n...\n\n## Concerns\n- ...\n\n## Verdict\nFound: 1 critical, 2 important, 3 suggestions\n\n**Request changes**",
|
||||||
|
"comments": [
|
||||||
|
{
|
||||||
|
"path": "path/to/file",
|
||||||
|
"line": 42,
|
||||||
|
"side": "RIGHT",
|
||||||
|
"start_line": 40,
|
||||||
|
"body": "⚠️ [IMPORTANT] Short explanation\n\n```suggestion\nreplacement\n```"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Field rules:
|
||||||
|
|
||||||
|
- `path` must be relative to the repository root.
|
||||||
|
- `line` is required and must target the correct side.
|
||||||
|
- `start_line` is optional and only for multi-line ranges.
|
||||||
|
- `side` must be `"LEFT"` or `"RIGHT"`.
|
||||||
|
|
||||||
|
## Summary Requirements
|
||||||
|
|
||||||
|
The `summary` must include:
|
||||||
|
|
||||||
|
- A high-level overview of the PR.
|
||||||
|
- Important concerns and any untouched-code concerns that could not be commented inline.
|
||||||
|
- Issue counts in the format `Found: X critical, Y important, Z suggestions`.
|
||||||
|
- A final recommendation of `Approve`, `Approve with nits`, or `Request changes`.
|
||||||
|
|
||||||
|
## Final Checks
|
||||||
|
|
||||||
|
Before finishing:
|
||||||
|
|
||||||
|
- Validate `review.json` with `jq`.
|
||||||
|
- Fix invalid JSON if validation fails.
|
||||||
|
- Confirm line numbers match the annotated diff.
|
||||||
|
- Do not run `gh pr review`, `gh pr comment`, `gh api`, or any other command that posts to GitHub.
|
||||||
|
|
||||||
|
Your only output is the final `review.json`.
|
||||||
@@ -0,0 +1,116 @@
|
|||||||
|
---
|
||||||
|
name: rust-unit-tests
|
||||||
|
description: Write, improve, and run Rust unit tests in the warp Rust codebase.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Rust Unit Tests in warp
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
- This skill focuses on crate-level unit tests.
|
||||||
|
- Favor incremental, well-scoped tests that exercise a single function or behavior per case.
|
||||||
|
|
||||||
|
## Where unit tests live
|
||||||
|
- Put unit tests in separate files named `${filename}_tests.rs` or `mod_test.rs`.
|
||||||
|
- Include the test module at the end of the corresponding source file:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
#[cfg(test)]
|
||||||
|
#[path = "filename_tests.rs"] // or "mod_test.rs"
|
||||||
|
mod tests;
|
||||||
|
```
|
||||||
|
|
||||||
|
## Writing good tests
|
||||||
|
- Use descriptive names: `fn parses_utf8_sequence_when_valid()`.
|
||||||
|
- Prefer `assert_eq!`/`assert_ne!` over `assert!` for clearer diffs.
|
||||||
|
- Use `#[should_panic]` only when panic semantics are intended API.
|
||||||
|
- Minimize global state; inject dependencies via traits/constructors to make logic testable without heavy mocking.
|
||||||
|
- When adding enums or expanding behavior, prefer exhaustive matches in code under test and mirror cases in tests.
|
||||||
|
- Be mindful of terminal model locking: avoid patterns that acquire multiple `model.lock()` calls in the same call stack from tests.
|
||||||
|
|
||||||
|
## Async and feature-gated code
|
||||||
|
- For async logic, use `#[tokio::test]` when the code requires a runtime.
|
||||||
|
- Prefer runtime feature checks (e.g., `FeatureFlag::X.is_enabled()`) over `#[cfg(...)]` so tests don’t require recompilation to toggle behavior.
|
||||||
|
|
||||||
|
## Quickstart harness (UI/model tests)
|
||||||
|
- Prefer `warpui::App::test` for deterministic unit tests around views/models.
|
||||||
|
- Initialize app models once, then mutate via `update` and assert via `read`.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use warpui::App;
|
||||||
|
// In app crate tests prefer `crate::test_util::...`; from other crates use `warp::test_util::...`.
|
||||||
|
use warp::test_util::{terminal::initialize_app_for_terminal_view, add_window_with_terminal};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn example() {
|
||||||
|
App::test((), |mut app| async move {
|
||||||
|
// One-time app setup for terminal/view tests
|
||||||
|
initialize_app_for_terminal_view(&mut app); // includes settings init
|
||||||
|
let term = add_window_with_terminal(&mut app, None);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
term.update(&mut app, |view, _ctx| {
|
||||||
|
view.model.lock().simulate_block("ls", "out");
|
||||||
|
});
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
term.read(&app, |view, _ctx| {
|
||||||
|
assert!(view.model.lock().block_list().len() > 0);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Common helpers to use
|
||||||
|
- Terminal model shortcuts: `TerminalModel::mock(..)`, `.simulate_block(..)`, `.finish_block()`, `.simulate_cmd(..)`.
|
||||||
|
- Builders for focused tests: `terminal::model::test_utils::{TestBlockListBuilder, TestBlockBuilder}`.
|
||||||
|
- Virtual filesystem for IO-heavy code:
|
||||||
|
```rust
|
||||||
|
use virtual_fs::{VirtualFS, Stub};
|
||||||
|
VirtualFS::test("case", |_dirs, mut fs| {
|
||||||
|
fs.with_files(vec![Stub::FileWithContent("path/file.txt", "contents")]);
|
||||||
|
// run logic and assert
|
||||||
|
});
|
||||||
|
```
|
||||||
|
- Feature flags (scoped):
|
||||||
|
```rust
|
||||||
|
use warp::features::FeatureFlag; // or `use crate::features::FeatureFlag;` inside the app crate
|
||||||
|
let _flag = FeatureFlag::CreatingSharedSessions.override_enabled(true);
|
||||||
|
```
|
||||||
|
- UI numeric assertions (lines):
|
||||||
|
```rust
|
||||||
|
assert_lines_approx_eq!(actual_lines, INLINE_BANNER_HEIGHT);
|
||||||
|
```
|
||||||
|
- Concurrency: keep `model.lock()` scopes minimal; avoid nested/re-entrant locks in the same call chain.
|
||||||
|
- Don’t call `initialize_settings_for_tests` directly when using `initialize_app_for_terminal_view` (it already calls it).
|
||||||
|
- Async needs: use `#[tokio::test]` when a real runtime is required; otherwise prefer `App::test`.
|
||||||
|
- Tests touching global/external state: consider `serial_test`'s `#[serial]` or local mocking instead of parallelism.
|
||||||
|
|
||||||
|
## Running unit tests
|
||||||
|
- Workspace (parallel):
|
||||||
|
```bash
|
||||||
|
cargo nextest run --no-fail-fast --workspace --exclude command-signatures-v2
|
||||||
|
```
|
||||||
|
- Single crate:
|
||||||
|
```bash
|
||||||
|
cargo nextest run -p <crate_name>
|
||||||
|
```
|
||||||
|
- Single test (filter by name):
|
||||||
|
```bash
|
||||||
|
cargo nextest run -E 'test(<substring>)'
|
||||||
|
```
|
||||||
|
- Doc tests:
|
||||||
|
```bash
|
||||||
|
cargo test --doc
|
||||||
|
```
|
||||||
|
|
||||||
|
## Linting and formatting
|
||||||
|
Run before submitting changes:
|
||||||
|
```bash
|
||||||
|
cargo fmt
|
||||||
|
cargo clippy --workspace --all-targets --all-features --tests -- -D warnings
|
||||||
|
```
|
||||||
|
|
||||||
|
For a full local check before a PR, you can also run:
|
||||||
|
```bash
|
||||||
|
./script/presubmit
|
||||||
|
```
|
||||||
@@ -0,0 +1,142 @@
|
|||||||
|
---
|
||||||
|
name: spec-driven-implementation
|
||||||
|
description: Drive a spec-first workflow for substantial features by writing PRODUCT.md before implementation, writing TECH.md when warranted, and keeping both specs updated as implementation evolves. Use when starting a significant feature, planning agent-driven implementation, or when the user wants product and tech specs checked into source control.
|
||||||
|
---
|
||||||
|
|
||||||
|
# spec-driven-implementation
|
||||||
|
|
||||||
|
Drive a spec-first workflow for substantial features in Warp.
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
Use this skill for significant features where a written spec will improve implementation quality, reduce ambiguity, or make review easier. Be pragmatic: not every change needs specs.
|
||||||
|
|
||||||
|
Specs should usually live in:
|
||||||
|
|
||||||
|
- `specs/<linear-ticket-number>/PRODUCT.md`
|
||||||
|
- `specs/<linear-ticket-number>/TECH.md`
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
- `specs/APP-1234/PRODUCT.md`
|
||||||
|
- `specs/APP-1234/TECH.md`
|
||||||
|
|
||||||
|
`specs/` should contain only ticket-named directories as direct children. Do not create engineer-named subdirectories or feature-slug directories there.
|
||||||
|
|
||||||
|
If a relevant Linear issue does not already exist, create one before writing specs. Use the Linear MCP tools directly:
|
||||||
|
|
||||||
|
- `list_teams` to find the appropriate team
|
||||||
|
- `list_issue_labels` to inspect the expected labels/tags
|
||||||
|
- `save_issue` to create the issue with the appropriate team and labels
|
||||||
|
|
||||||
|
If the correct team or labels are not obvious from the request and surrounding context, use `ask_user_question` to clarify rather than guessing.
|
||||||
|
|
||||||
|
These specs should largely be written by agents, not by hand, and should be checked into source control so they can be reviewed and kept current with the code.
|
||||||
|
|
||||||
|
## When specs are required
|
||||||
|
|
||||||
|
Strongly prefer specs when the change is substantial, such as:
|
||||||
|
|
||||||
|
- product or architectural ambiguity
|
||||||
|
- expected implementation size around 1k+ LOC
|
||||||
|
- deep or cross-cutting stack changes
|
||||||
|
- risky behavior changes where regressions would be expensive
|
||||||
|
- work where agent quality will improve materially from clearer inputs
|
||||||
|
|
||||||
|
Specs are often unnecessary for:
|
||||||
|
|
||||||
|
- small, local bug fixes
|
||||||
|
- straightforward refactors
|
||||||
|
- narrow UI tweaks with little ambiguity
|
||||||
|
|
||||||
|
For pure UI changes, the product spec is often useful while the tech spec may be unnecessary.
|
||||||
|
|
||||||
|
## Workflow
|
||||||
|
|
||||||
|
### 1. Decide whether the feature needs specs
|
||||||
|
|
||||||
|
Evaluate the size, ambiguity, and risk of the feature. If specs will not meaningfully improve execution or review, skip them and focus on verification instead.
|
||||||
|
|
||||||
|
### 2. Write the product spec first
|
||||||
|
|
||||||
|
Before implementation, create `PRODUCT.md` describing the desired user-facing behavior.
|
||||||
|
|
||||||
|
Use the `write-product-spec` skill to produce it. The product spec should define:
|
||||||
|
|
||||||
|
- what problem is being solved
|
||||||
|
- the desired user experience
|
||||||
|
- invariants and edge cases
|
||||||
|
- success criteria
|
||||||
|
- how the behavior will be validated
|
||||||
|
|
||||||
|
If the feature has UI or interaction design, ask for a Figma mock if one exists. If there is no mock, continue but call that out explicitly in the product spec.
|
||||||
|
|
||||||
|
Reference the Linear issue in the spec when one exists. Because specs live under `specs/<linear-ticket-number>/...`, this should usually be straightforward.
|
||||||
|
|
||||||
|
### 3. Write the tech spec when warranted
|
||||||
|
|
||||||
|
Use the `write-tech-spec` skill for substantial or ambiguous implementation work.
|
||||||
|
|
||||||
|
Prefer a tech spec when:
|
||||||
|
|
||||||
|
- the implementation spans multiple subsystems
|
||||||
|
- architecture or extensibility matters
|
||||||
|
- there are meaningful tradeoffs to document
|
||||||
|
- reviewers will benefit more from reviewing the plan than the raw code
|
||||||
|
|
||||||
|
It is acceptable to write the tech spec after an e2e prototype if that leads to a more accurate implementation plan. Do not force a premature tech spec when the implementation details are still too uncertain.
|
||||||
|
|
||||||
|
### 4. Implement approved specs
|
||||||
|
|
||||||
|
After the specs are approved, use the `implement-specs` skill to build from the approved `PRODUCT.md` and `TECH.md`.
|
||||||
|
|
||||||
|
The implementation can often be pushed in the same PR as the product and tech specs. As the engineer iterates, keep `PRODUCT.md`, `TECH.md`, code changes, and tests in that same PR so the review reflects the feature that will actually ship.
|
||||||
|
|
||||||
|
For large features, the implementer may optionally offer:
|
||||||
|
|
||||||
|
- `PROJECT_LOG.md` to track explored paths, checkpoints, and current implementation state
|
||||||
|
- `DECISIONS.md` to capture concrete product and technical decisions made during design and implementation
|
||||||
|
|
||||||
|
These are optional aids, not required outputs.
|
||||||
|
|
||||||
|
### 5. Keep specs current during implementation
|
||||||
|
|
||||||
|
If implementation changes from the spec, update the spec rather than leaving it stale.
|
||||||
|
|
||||||
|
Update `PRODUCT.md` when:
|
||||||
|
|
||||||
|
- user-facing behavior changes
|
||||||
|
- success criteria change
|
||||||
|
- UX details or edge cases change
|
||||||
|
|
||||||
|
Update `TECH.md` when:
|
||||||
|
|
||||||
|
- the implementation approach changes
|
||||||
|
- architectural boundaries move
|
||||||
|
- risks, dependencies, or rollout details change
|
||||||
|
- the testing or validation plan changes
|
||||||
|
|
||||||
|
The checked-in specs should describe the feature that actually ships, not just the initial intent. Keep those spec updates in the same PR as the related code changes whenever practical.
|
||||||
|
|
||||||
|
### 6. Verify behavior against the spec
|
||||||
|
|
||||||
|
Before considering the work complete, make sure verification maps back to the specs. Prefer tests and artifacts that validate the product behavior directly:
|
||||||
|
|
||||||
|
- use the `rust-unit-tests` skill for crate-level unit tests and regression coverage
|
||||||
|
- integration tests for critical user flows
|
||||||
|
- loom walkthroughs or equivalent feature demonstrations when appropriate
|
||||||
|
- screenshots or videos when useful for UI-heavy work
|
||||||
|
|
||||||
|
## Best Practices
|
||||||
|
|
||||||
|
- Be pragmatic above all else.
|
||||||
|
- Write specs to improve input quality for agents, not as ceremony.
|
||||||
|
- Keep product specs behavior-oriented and implementation-light.
|
||||||
|
- Keep tech specs implementation-oriented and grounded in current codebase patterns.
|
||||||
|
- Use review time to validate specs and behavior, not to over-index on code style nits.
|
||||||
|
|
||||||
|
## Related Skills
|
||||||
|
|
||||||
|
- `implement-specs`
|
||||||
|
- `write-product-spec`
|
||||||
|
- `write-tech-spec`
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
---
|
||||||
|
name: triage-issue-local
|
||||||
|
specializes: triage-issue
|
||||||
|
description: Repo-specific triage guidance for warp-external. Only the categories declared overridable by the core triage-issue skill may be specialized here.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Repo-specific triage guidance for `warp-external`
|
||||||
|
|
||||||
|
This file is a companion to the core `triage-issue` skill. It does not
|
||||||
|
redefine the triage output schema, safety rules, or follow-up-question
|
||||||
|
contract. It only specializes the override categories the core skill
|
||||||
|
marks as overridable.
|
||||||
|
|
||||||
|
## Heuristics
|
||||||
|
|
||||||
|
- `warp-external` is the public-facing Warp desktop client repository. Treat public issue reports as potentially incomplete and avoid asking for secrets, tokens, private workspace names, private repository names, or account identifiers in the public issue thread.
|
||||||
|
- Distinguish the user's observed Warp behavior from their guesses about Rust modules, UI components, server behavior, feature flags, or product intent.
|
||||||
|
- For issue reports that mention another terminal, editor, shell, or CLI tool, identify whether the problem is Warp-specific or generally reproducible outside Warp before assigning Warp ownership.
|
||||||
|
- When the issue includes screenshots, videos, logs, stack traces, or command output, use them as primary evidence and ask follow-up questions only for missing details that cannot be inferred from that evidence.
|
||||||
|
- Before asking any follow-up questions, check the Warp documentation and the repository's existing feature set to determine whether the desired behavior the reporter is describing is already supported. If an existing feature, setting, or workflow satisfies the request, recommend it to the reporter instead of treating the issue as a bug or feature gap.
|
||||||
|
- If the report is about billing (pricing, plans, subscriptions, payments, refunds, invoices, AI request quotas, charges) or about appeals (account suspensions, bans, takedowns, abuse decisions, or other account-status disputes), do not attempt to triage it as an actionable bug or feature request. Instead, notify the reporter that these requests must go through Warp's support channels (https://docs.warp.dev/support-and-community/troubleshooting-and-support/sending-us-feedback) and direct them there for resolution. Apply the relevant `area:billing` or `area:auth` label as appropriate so the issue is still routed correctly.
|
||||||
|
|
||||||
|
## Follow-up question limit
|
||||||
|
|
||||||
|
Ask **at most 2 follow-up questions** per triage response. Each question must be high-value: it should meaningfully change the label assignment, owner routing, or reproduction confidence if answered. Do not ask questions whose answers can be inferred from existing evidence, and do not bundle multiple sub-questions into a single bullet. If more than 2 unknowns exist, prioritize the two that are most likely to unblock triage.
|
||||||
|
|
||||||
|
## Label taxonomy
|
||||||
|
|
||||||
|
The label taxonomy for this repository is managed in `.github/issue-triage/config.json`. Prefer labels from that configuration, especially the `area:*`, `os:*`, `repro:*`, `accessibility`, `needs-info`, `duplicate`, and primary issue-type labels. Do not invent new labels unless the prompt explicitly allows it.
|
||||||
|
|
||||||
|
Use area labels based on the user's reported surface:
|
||||||
|
|
||||||
|
- `area:shell-terminal` for terminal output, block rendering, shell integration, prompt rendering, command execution display, and terminal-emulation behavior.
|
||||||
|
- `area:terminal-input` for command-line input editing, cursor movement, key handling, and typed text behavior.
|
||||||
|
- `area:window-tabs-panes` for window, tab, pane, split, layout, and focus behavior.
|
||||||
|
- `area:editor-notebooks` for editors, notebooks, markdown rendering, LSP, and code display.
|
||||||
|
- `area:agent` for agent conversations, agent mode, cloud/local agent execution, prompts, and AI-specific UI.
|
||||||
|
- `area:code-review` for git diff views, review UI, review comments, and PR-focused agent flows.
|
||||||
|
- `area:mcp` for MCP server connection, tool/resource discovery, OAuth, and integration issues.
|
||||||
|
- `area:settings-keybindings` for settings UI, preferences, keyboard shortcuts, and keybinding configuration.
|
||||||
|
- `area:warp-drive` for Warp Drive objects, sync, sharing, workflows, notebooks, tab configs, and persisted artifacts.
|
||||||
|
- `area:performance:*` when the report includes CPU, memory, GPU, startup, rendering, latency, or responsiveness symptoms. Add the more specific CPU, memory, or GPU label when the evidence points to that resource.
|
||||||
|
|
||||||
|
## Information to check for before asking follow-up questions
|
||||||
|
|
||||||
|
Before asking the reporter for more information, check the issue body, comments, attachments, logs, labels, and repository context for:
|
||||||
|
|
||||||
|
- Warp channel and version/build number, especially whether the report is for Dev, Canary, Preview, Beta, or Stable.
|
||||||
|
- OS and version, architecture, display setup, window manager or desktop environment on Linux, and whether the issue is platform-specific.
|
||||||
|
- Shell and terminal context: shell name/version, prompt framework, shell integration status, command being run, terminal mode, local vs SSH/remote/tmux, and whether the behavior reproduces in a fresh session.
|
||||||
|
- Clear reproduction steps, expected behavior, actual behavior, frequency, regression timing, and whether the user can reproduce outside Warp.
|
||||||
|
- Visual evidence for UI, rendering, layout, font, cursor, focus, window, pane, tab, and accessibility issues. Prefer a screenshot or short recording when the symptom is visual.
|
||||||
|
- Logs and diagnostics for crashes, hangs, startup failures, update failures, authentication failures, MCP failures, and agent execution failures. Ask for redacted logs only when the report lacks actionable evidence.
|
||||||
|
- For AI/agent reports: whether the agent is local or cloud, the model if known, relevant conversation/session link, repository context, tool or MCP server involved, and the exact user action that triggered the failure.
|
||||||
|
- For performance reports: approximate project/session size, command output size, CPU/memory/GPU observations, profile or diagnostics if provided, and whether the issue appears after long-running sessions.
|
||||||
|
- For keyboard or input reports: keyboard layout, custom keybindings, IME usage, conflicting OS shortcuts, focused surface, and whether the same keys work in other apps.
|
||||||
|
- For account, billing, or auth reports: account tier or authentication method only if the user already provided it. Do not ask for private identifiers in public; direct the user to support when private account details are required. For billing or appeals reports specifically, do not pursue further triage questions in the public thread—redirect the reporter to Warp's support channels per the heuristic above.
|
||||||
|
|
||||||
|
## Recurring follow-up patterns
|
||||||
|
|
||||||
|
- Visual UI/rendering issue with no media: ask for a screenshot or short screen recording first.
|
||||||
|
- Environment-sensitive terminal issue: ask for Warp version/channel, OS/version, shell, and whether it reproduces in a fresh local session.
|
||||||
|
- SSH/tmux/remote issue: ask for local OS, remote OS, shell, whether tmux is involved, and the minimal command or workflow that reproduces it.
|
||||||
|
- Agent/MCP issue: ask for the failing workflow, local vs cloud execution, relevant session link, MCP server/tool name, and any redacted error text.
|
||||||
|
- Performance issue: ask for approximate scale, how long Warp has been running, what action triggers the spike or hang, and whether logs or a profile are available.
|
||||||
|
|
||||||
|
## Owner-inference hints
|
||||||
|
|
||||||
|
Prefer `.github/STAKEHOLDERS` for owner inference. When no path-level match exists, use the label and issue surface to choose likely owners rather than defaulting to broad app ownership.
|
||||||
@@ -0,0 +1,114 @@
|
|||||||
|
---
|
||||||
|
name: update-skill
|
||||||
|
description: Create or update skills by generating, editing, or refining SKILL.md files in this repository. Use when authoring new skills or revising the structure, frontmatter, or guidance for existing ones.
|
||||||
|
---
|
||||||
|
|
||||||
|
# update-skill
|
||||||
|
|
||||||
|
This guide provides instructions for creating or updating skills in this repository. It covers the required structure, frontmatter, and best practices for skills.
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
Every skill is a directory containing a `SKILL.md` file with YAML frontmatter and markdown body:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
---
|
||||||
|
name: pdf-processing
|
||||||
|
description: Extract text and tables from PDF files, fill forms, merge documents.
|
||||||
|
---
|
||||||
|
|
||||||
|
# PDF Processing
|
||||||
|
|
||||||
|
## When to use this skill
|
||||||
|
Use this skill when the user needs to work with PDF files...
|
||||||
|
|
||||||
|
## How to extract text
|
||||||
|
1. Use pdfplumber for text extraction...
|
||||||
|
|
||||||
|
## How to fill forms
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
### Frontmatter (Required)
|
||||||
|
|
||||||
|
Every SKILL.md must start with YAML frontmatter containing:
|
||||||
|
|
||||||
|
- **name**: Kebab-case identifier (lowercase letters, numbers, hyphens only)
|
||||||
|
- Example: `add-feature-flag`, `rust-unit-tests`, `update-skill`
|
||||||
|
- **description**: Specific description of what the skill does and when to use it
|
||||||
|
- Must be non-empty
|
||||||
|
- Should include key terms for skill discovery
|
||||||
|
- Begin with an action verb to clearly state what the skill accomplishes (e.g., "Adds feature flags..." instead of "Helps with features..."), and immediately follow with a specific use case or context (e.g., "Use when working with feature flags")
|
||||||
|
- Write in third person (e.g., "Adds feature flags..." not "I can help you add...")
|
||||||
|
|
||||||
|
### Writing Effective Descriptions
|
||||||
|
|
||||||
|
The description field is critical for skill discovery. Include both **what** the skill does and **when** to use it. Some good examples:
|
||||||
|
|
||||||
|
- `git-commit`: "Generate descriptive commit messages by analyzing git diffs. Use when the user asks for help writing commit messages or reviewing staged changes."
|
||||||
|
- `pdf-processing`: "Extract text and tables from PDF files, fill forms, merge documents. Use when working with PDF files or when the user mentions PDFs, forms, or document extraction."
|
||||||
|
|
||||||
|
Avoid vague descriptions like "Helps with code" or "Does development tasks". For more context, see "Description Best Practices" in [references/best-practices.md](references/best-practices.md).
|
||||||
|
|
||||||
|
### Skill Structure
|
||||||
|
|
||||||
|
Typical sections in Warp skills:
|
||||||
|
|
||||||
|
1. **Title and brief summary** – Clear title and a concise overview of the skill's purpose and primary use cases. Link to sections, reference files or related skills if useful
|
||||||
|
2. **Overview** - Context about the skill's purpose (optional but common), extends the summary with more details and context
|
||||||
|
3. **Main content** - Steps, usage instructions, or workflow guidance
|
||||||
|
4. **Best Practices** - Guidelines and recommendations (optional)
|
||||||
|
5. **Examples / Reference PRs** - Links to real examples (optional)
|
||||||
|
|
||||||
|
Keep the structure flexible based on the skill's needs. Simple skills can omit the optional sections.
|
||||||
|
|
||||||
|
### Validation
|
||||||
|
|
||||||
|
Optionally, use the [skills-ref](https://github.com/agentskills/agentskills/tree/main/skills-ref) reference library to validate your skills:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
skills-ref validate ./my-skill
|
||||||
|
```
|
||||||
|
|
||||||
|
This checks that your SKILL.md frontmatter is valid and follows all naming conventions. If not installed, use the WebSearch tool to get context around this package.
|
||||||
|
|
||||||
|
### Main Content Best Practices
|
||||||
|
|
||||||
|
- For guidance on what qualifies as good main content, see "Conciseness Principles" in [references/best-practices.md](references/best-practices.md)
|
||||||
|
- When formatting code examples, see "Code Example Formatting" in [references/best-practices.md](references/best-practices.md).
|
||||||
|
|
||||||
|
### File Organization
|
||||||
|
|
||||||
|
- **Simple skills** (<=200 lines): Keep everything in SKILL.md
|
||||||
|
- **Complex skills** (>200 lines): Split detailed content into `references/` subdirectory
|
||||||
|
- Reference files from SKILL.md with clear links
|
||||||
|
- Example: "See [references/best-practices.md](references/best-practices.md) for detailed guidance"
|
||||||
|
|
||||||
|
## When to Split Content
|
||||||
|
|
||||||
|
Create `references/` subdirectory when:
|
||||||
|
|
||||||
|
- SKILL.md approaches 200+ lines
|
||||||
|
- Skill covers multiple domains or workflows that can be loaded independently
|
||||||
|
- Detailed reference material would clutter the main instructions
|
||||||
|
|
||||||
|
Keep only essential workflow and procedural instructions in SKILL.md. Move detailed reference material, schemas, and extensive examples to `references/` files.
|
||||||
|
|
||||||
|
## Examples from Existing Skills
|
||||||
|
|
||||||
|
For reference on structure and style:
|
||||||
|
|
||||||
|
- `.agents/skills/add-feature-flag/SKILL.md` - Multi-step workflow with clear sequential steps
|
||||||
|
- `.agents/skills/rust-unit-tests/SKILL.md` - Comprehensive guide with code examples and helper utilities
|
||||||
|
- `.agents/skills/remove-feature-flag/SKILL.md` - Cleanup workflow with search commands
|
||||||
|
|
||||||
|
## Best Practices
|
||||||
|
|
||||||
|
See [references/best-practices.md](references/best-practices.md) for detailed authoring guidance including:
|
||||||
|
|
||||||
|
- Progressive disclosure patterns
|
||||||
|
- Writing concise, effective instructions
|
||||||
|
- Code example formatting
|
||||||
|
- Common anti-patterns to avoid
|
||||||
@@ -0,0 +1,310 @@
|
|||||||
|
# Best Practices for Warp Skills
|
||||||
|
|
||||||
|
Detailed authoring guidance for creating effective skills in `.agents/skills/`.
|
||||||
|
|
||||||
|
## Progressive Disclosure
|
||||||
|
|
||||||
|
Skills use a loading system to manage context efficiently:
|
||||||
|
|
||||||
|
1. **Metadata (name + description)** - Always loaded at startup
|
||||||
|
2. **SKILL.md body** - Loaded when skill triggers
|
||||||
|
3. **Reference files** - Loaded only when needed
|
||||||
|
|
||||||
|
### When to Use References
|
||||||
|
|
||||||
|
Keep SKILL.md under 150-200 lines. When content grows beyond this:
|
||||||
|
|
||||||
|
**Pattern 1: High-level guide with references**
|
||||||
|
|
||||||
|
SKILL.md contains the core workflow and points to detailed references:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
## Advanced Features
|
||||||
|
|
||||||
|
- **Detailed configuration**: See [references/config.md](references/config.md)
|
||||||
|
- **API reference**: See [references/api.md](references/api.md)
|
||||||
|
- **Examples**: See [references/examples.md](references/examples.md)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Pattern 2: Domain-specific organization**
|
||||||
|
|
||||||
|
For skills with multiple independent domains, organize by domain:
|
||||||
|
|
||||||
|
```
|
||||||
|
skill-name/
|
||||||
|
├── SKILL.md (overview and navigation)
|
||||||
|
└── references/
|
||||||
|
├── domain-a.md
|
||||||
|
├── domain-b.md
|
||||||
|
└── domain-c.md
|
||||||
|
```
|
||||||
|
|
||||||
|
When the user works with domain-a, the agent only loads domain-a.md, not the others.
|
||||||
|
|
||||||
|
**Pattern 3: Conditional details**
|
||||||
|
|
||||||
|
Show basic content inline, link to advanced content:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
## Basic Usage
|
||||||
|
|
||||||
|
[Core instructions here]
|
||||||
|
|
||||||
|
**For advanced configuration**: See [references/advanced.md](references/advanced.md)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Important Guidelines
|
||||||
|
|
||||||
|
- **Keep references one level deep** - All reference files should link directly from SKILL.md
|
||||||
|
- **Avoid nested references** - Don't create references that reference other files
|
||||||
|
- **Add table of contents** - For reference files >100 lines, include TOC at the top
|
||||||
|
|
||||||
|
## Writing Effective Descriptions
|
||||||
|
|
||||||
|
The description field enables skill discovery. the agent uses it to decide when to load the skill.
|
||||||
|
|
||||||
|
### Description Best Practices
|
||||||
|
|
||||||
|
1. **Be specific and include key terms**
|
||||||
|
- Good: "Add a new feature flag to gate code changes in the Warp codebase."
|
||||||
|
- Avoid: "Helps with features."
|
||||||
|
|
||||||
|
2. **Include both what and when**
|
||||||
|
- What the skill does: "Write, improve, and run Rust unit tests"
|
||||||
|
- When to use it: "in the warp Rust codebase"
|
||||||
|
|
||||||
|
3. **Write in third person**
|
||||||
|
- Good: "Adds feature flags to gate code changes"
|
||||||
|
- Avoid: "I can help you add feature flags"
|
||||||
|
- Avoid: "You can use this to add feature flags"
|
||||||
|
|
||||||
|
4. **Include trigger terms**
|
||||||
|
- Mention specific files, commands, or concepts
|
||||||
|
- Example: "Use when working with PDF files, forms, or document extraction"
|
||||||
|
|
||||||
|
## Conciseness Principles
|
||||||
|
|
||||||
|
Context window is shared across all skills, conversation history, and the system prompt. Every token matters.
|
||||||
|
|
||||||
|
### Default Assumption: Agent is Already Smart
|
||||||
|
|
||||||
|
Only add context the agent doesn't already have. Challenge each piece:
|
||||||
|
|
||||||
|
- "Does the agent really need this explanation?"
|
||||||
|
- "Can I assume the agent knows this?"
|
||||||
|
- "Does this paragraph justify its token cost?"
|
||||||
|
|
||||||
|
**Good (concise):**
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
## Extract PDF text
|
||||||
|
|
||||||
|
Use pdfplumber for text extraction:
|
||||||
|
|
||||||
|
\`\`\`python
|
||||||
|
import pdfplumber
|
||||||
|
|
||||||
|
with pdfplumber.open("file.pdf") as pdf:
|
||||||
|
text = pdf.pages[0].extract_text()
|
||||||
|
\`\`\`
|
||||||
|
```
|
||||||
|
|
||||||
|
**Bad (verbose):**
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
## Extract PDF text
|
||||||
|
|
||||||
|
PDF (Portable Document Format) files are a common file format that contains
|
||||||
|
text, images, and other content. To extract text from a PDF, you'll need to
|
||||||
|
use a library. There are many libraries available for PDF processing, but we
|
||||||
|
recommend pdfplumber because it's easy to use and handles most cases well.
|
||||||
|
First, you'll need to install it using pip. Then you can use the code below...
|
||||||
|
```
|
||||||
|
|
||||||
|
The concise version assumes the agent knows what PDFs are and how libraries work.
|
||||||
|
|
||||||
|
## Code Example Formatting
|
||||||
|
|
||||||
|
### Syntax Highlighting
|
||||||
|
|
||||||
|
Always specify the language for code blocks:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
pub fn example() {
|
||||||
|
println!("Always specify language");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo nextest run --workspace
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example Structure
|
||||||
|
|
||||||
|
For workflow-based skills, show before/after or step-by-step:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
### Before:
|
||||||
|
\`\`\`rust
|
||||||
|
if FeatureFlag::YourFeature.is_enabled() {
|
||||||
|
// new behavior
|
||||||
|
} else {
|
||||||
|
// old behavior (dead code)
|
||||||
|
}
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
### After:
|
||||||
|
\`\`\`rust
|
||||||
|
// new behavior (unconditionally enabled)
|
||||||
|
\`\`\`
|
||||||
|
```
|
||||||
|
|
||||||
|
### Inline Commands
|
||||||
|
|
||||||
|
For shell commands, show the complete command with flags:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo clippy --workspace --all-targets --all-features --tests -- -D warnings
|
||||||
|
```
|
||||||
|
|
||||||
|
Explain non-obvious flags if necessary, but prefer self-documenting commands.
|
||||||
|
|
||||||
|
## Workflows vs Simple Instructions
|
||||||
|
|
||||||
|
### When to Use Workflows
|
||||||
|
|
||||||
|
Use numbered steps for multi-step processes where order matters:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
## Workflow
|
||||||
|
|
||||||
|
1. Analyze the form structure
|
||||||
|
2. Create field mapping
|
||||||
|
3. Validate mapping
|
||||||
|
4. Fill the form
|
||||||
|
5. Verify output
|
||||||
|
```
|
||||||
|
|
||||||
|
Include a checklist for complex workflows:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
Copy this checklist and track progress:
|
||||||
|
|
||||||
|
\`\`\`
|
||||||
|
Task Progress:
|
||||||
|
- [ ] Step 1: Analyze form
|
||||||
|
- [ ] Step 2: Create mapping
|
||||||
|
- [ ] Step 3: Validate
|
||||||
|
- [ ] Step 4: Fill form
|
||||||
|
- [ ] Step 5: Verify
|
||||||
|
\`\`\`
|
||||||
|
```
|
||||||
|
|
||||||
|
### When to Use Simple Instructions
|
||||||
|
|
||||||
|
For straightforward tasks, skip the workflow structure:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
## Adding a Feature Flag
|
||||||
|
|
||||||
|
Add the feature to `app/Cargo.toml`:
|
||||||
|
|
||||||
|
\`\`\`toml
|
||||||
|
[features]
|
||||||
|
your_feature_name = []
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
Then gate code with the runtime check:
|
||||||
|
|
||||||
|
\`\`\`rust
|
||||||
|
if FeatureFlag::YourFeatureName.is_enabled() {
|
||||||
|
// feature-gated behavior
|
||||||
|
}
|
||||||
|
\`\`\`
|
||||||
|
```
|
||||||
|
|
||||||
|
## Common Anti-Patterns
|
||||||
|
|
||||||
|
### ❌ Windows-Style Paths
|
||||||
|
|
||||||
|
Always use forward slashes:
|
||||||
|
|
||||||
|
- ✓ Good: `scripts/helper.py`, `references/guide.md`
|
||||||
|
- ✗ Avoid: `scripts\helper.py`, `references\guide.md`
|
||||||
|
|
||||||
|
### ❌ Vague Descriptions
|
||||||
|
|
||||||
|
Be specific:
|
||||||
|
|
||||||
|
- ✗ Avoid: "Helps with documents"
|
||||||
|
- ✓ Good: "Extract text and tables from PDF files"
|
||||||
|
|
||||||
|
### ❌ Too Many Options
|
||||||
|
|
||||||
|
Don't present multiple approaches unless necessary:
|
||||||
|
|
||||||
|
- ✗ Avoid: "You can use pypdf, or pdfplumber, or PyMuPDF, or..."
|
||||||
|
- ✓ Good: "Use pdfplumber for text extraction. For scanned PDFs requiring OCR, use pdf2image with pytesseract instead."
|
||||||
|
|
||||||
|
### ❌ Time-Sensitive Information
|
||||||
|
|
||||||
|
Don't include dates or version-specific guidance:
|
||||||
|
|
||||||
|
- ✗ Avoid: "If you're doing this before August 2025, use the old API."
|
||||||
|
- ✓ Good: Use a "Current method" and "Old patterns" section with deprecation notes
|
||||||
|
|
||||||
|
### ❌ Inconsistent Terminology
|
||||||
|
|
||||||
|
Choose one term and use it throughout:
|
||||||
|
|
||||||
|
- ✗ Avoid: Mix "API endpoint", "URL", "API route", "path"
|
||||||
|
- ✓ Good: Always "API endpoint"
|
||||||
|
|
||||||
|
### ❌ Explaining the Obvious
|
||||||
|
|
||||||
|
Skip explanations for concepts the agent already knows:
|
||||||
|
|
||||||
|
- ✗ Avoid: "Git is a version control system that tracks changes in files..."
|
||||||
|
- ✓ Good: "Use `git --no-pager diff` to see changes without pagination"
|
||||||
|
|
||||||
|
### ❌ Over-Structuring Simple Skills
|
||||||
|
|
||||||
|
Not every skill needs an Overview, Best Practices, and Examples section. Use only what adds value:
|
||||||
|
|
||||||
|
- Simple skills: Title + instructions
|
||||||
|
- Medium skills: Title + Overview + instructions
|
||||||
|
- Complex skills: Full structure with multiple sections
|
||||||
|
|
||||||
|
## Naming Conventions
|
||||||
|
|
||||||
|
Use consistent naming patterns for skills:
|
||||||
|
|
||||||
|
**Recommended: Gerund form (verb + -ing)**
|
||||||
|
|
||||||
|
- `processing-pdfs`
|
||||||
|
- `analyzing-spreadsheets`
|
||||||
|
- `managing-databases`
|
||||||
|
- `testing-code`
|
||||||
|
|
||||||
|
**Acceptable alternatives:**
|
||||||
|
|
||||||
|
- Noun phrases: `pdf-processing`, `spreadsheet-analysis`
|
||||||
|
- Action-oriented: `process-pdfs`, `analyze-spreadsheets`
|
||||||
|
|
||||||
|
**Avoid:**
|
||||||
|
|
||||||
|
- Vague names: `helper`, `utils`, `tools`
|
||||||
|
- Overly generic: `documents`, `data`, `files`
|
||||||
|
|
||||||
|
Consistent naming makes skills easier to reference, understand at a glance, and organize.
|
||||||
|
|
||||||
|
## Skill Iteration
|
||||||
|
|
||||||
|
Skills improve through usage. When updating a skill:
|
||||||
|
|
||||||
|
1. **Observe usage** - Note where the agent struggles or succeeds
|
||||||
|
2. **Identify gaps** - What information was missing or unclear?
|
||||||
|
3. **Update targeted sections** - Fix specific issues without over-explaining
|
||||||
|
4. **Test changes** - Use the skill on similar tasks to verify improvements
|
||||||
|
|
||||||
|
Keep iterations focused. Don't add content preemptively—only add what's proven necessary through real usage.
|
||||||
@@ -0,0 +1,387 @@
|
|||||||
|
---
|
||||||
|
name: warp-integration-test
|
||||||
|
description: Writes, runs, and debugs Warp integration tests using the custom Builder/TestStep framework in `crates/integration`. Use when adding a new integration test, fixing a failing integration test, wiring a test into the manual runner or nextest suite, or verifying end-to-end UI and terminal behavior in Warp.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Warp Integration Tests
|
||||||
|
|
||||||
|
Use this skill for Rust integration tests in Warp's custom framework under `crates/integration/`.
|
||||||
|
|
||||||
|
These are not ordinary unit tests. They boot a real Warp app instance, give it an isolated test home directory, drive it with synthetic UI and terminal events, and poll assertions until success or timeout.
|
||||||
|
|
||||||
|
## Framework map
|
||||||
|
|
||||||
|
The core pieces are:
|
||||||
|
|
||||||
|
- `crates/integration/src/bin/integration.rs`
|
||||||
|
- Manual integration test runner binary.
|
||||||
|
- Registers test names to `Builder` factories.
|
||||||
|
- Runs exactly one named test per invocation.
|
||||||
|
- `crates/integration/tests/common/mod.rs`
|
||||||
|
- The outer Rust test harness used by `cargo test` and `cargo nextest`.
|
||||||
|
- Shells out to the integration binary.
|
||||||
|
- Forwards a limited set of env vars (`PATH`, `RUST_*`, `WARP_*`, `WARPUI_*`, `WGPU_*`, display-related vars).
|
||||||
|
- Re-runs tests up to 10 times when the integration binary exits with the special rerun code.
|
||||||
|
- `crates/integration/src/test.rs`
|
||||||
|
- Module hub for integration tests.
|
||||||
|
- Add new test modules here and `pub use` their functions so the runner can see them.
|
||||||
|
- `crates/integration/tests/integration/ui_tests.rs`
|
||||||
|
- List of UI-oriented integration tests that nextest should run.
|
||||||
|
- `crates/integration/tests/integration/shell_integration_tests.rs`
|
||||||
|
- List of tests that must run against every shell or a specific shell matrix.
|
||||||
|
- `crates/integration/src/builder.rs`
|
||||||
|
- Warp-specific wrapper around the lower-level WarpUI integration builder.
|
||||||
|
- Sets default timeout, hermetic home directory, shell rc files, user prefs, and real-display mode when requested.
|
||||||
|
- `crates/warpui_core/src/integration/driver.rs`
|
||||||
|
- Executes steps, handles retries, precondition reruns, screenshots, video capture, artifact export, and `on_finish`.
|
||||||
|
- `crates/warpui_core/src/integration/step.rs`
|
||||||
|
- Defines `TestStep`, input/event APIs, assertion polling, step-to-step data passing, and screenshot/recording hooks.
|
||||||
|
- `app/src/integration_testing/`
|
||||||
|
- High-level helpers and assertions for common Warp behaviors.
|
||||||
|
- Prefer these helpers over raw low-level event plumbing whenever they fit.
|
||||||
|
|
||||||
|
## How the framework actually runs a test
|
||||||
|
|
||||||
|
1. A Rust test from `crates/integration/tests/integration/*.rs` calls `run_integration_test("test_name")`.
|
||||||
|
2. That harness launches the `integration` binary with the test name.
|
||||||
|
3. The binary in `crates/integration/src/bin/integration.rs` looks up the name in `register_tests()`, builds the `Builder`, and turns it into a `TestDriver`.
|
||||||
|
4. `Builder::build(...)` creates an isolated temp directory, points `HOME` at it, writes minimal rc files, and initializes file-backed user preferences.
|
||||||
|
5. The driver runs each `TestStep` in order:
|
||||||
|
- setup callbacks
|
||||||
|
- synthetic events
|
||||||
|
- actions
|
||||||
|
- assertion polling until success or timeout
|
||||||
|
6. If an assertion returns `PreconditionFailed`, the binary exits with the rerun code and the outer harness retries the whole test.
|
||||||
|
7. On success, failure, or cancellation, the driver can run `on_finish` and export artifacts/runtime tags.
|
||||||
|
|
||||||
|
This means integration tests should be written for a hermetic environment. Do not rely on the developer's real shell dotfiles, home directory contents, or persisted Warp settings.
|
||||||
|
|
||||||
|
## Where to put a new test
|
||||||
|
|
||||||
|
Add the actual test function in a module under `crates/integration/src/test/`.
|
||||||
|
|
||||||
|
Use these heuristics:
|
||||||
|
|
||||||
|
- Put the test in an existing module when it matches that feature area.
|
||||||
|
- Create a new module when the feature does not fit an existing one cleanly.
|
||||||
|
- Add the test to `crates/integration/tests/integration/ui_tests.rs` if it is primarily a UI/app behavior test.
|
||||||
|
- Add the test to `crates/integration/tests/integration/shell_integration_tests.rs` if it needs to run against every shell, or depends on a specific shell/set of shells.
|
||||||
|
|
||||||
|
Being present in `crates/integration/src/test/*.rs` is not enough. For a test to run under `cargo nextest`, it also needs to be listed in one of the macro files in `crates/integration/tests/integration/`.
|
||||||
|
|
||||||
|
## Authoring checklist for a new test
|
||||||
|
|
||||||
|
When adding a new integration test, do all of the following:
|
||||||
|
|
||||||
|
1. Implement `pub fn test_name() -> Builder` in a module under `crates/integration/src/test/`.
|
||||||
|
2. Add the module to `crates/integration/src/test.rs`.
|
||||||
|
3. `pub use` the new module's exports from `crates/integration/src/test.rs`.
|
||||||
|
4. Add `register_test!(test_name);` in `crates/integration/src/bin/integration.rs`.
|
||||||
|
5. Add `test_name` to either:
|
||||||
|
- `crates/integration/tests/integration/ui_tests.rs`, or
|
||||||
|
- `crates/integration/tests/integration/shell_integration_tests.rs`
|
||||||
|
6. Default to making the test run in CI once it is added to one of those macro lists. Only mark it `#[ignore]` when the task explicitly calls for manual-only coverage or there is a concrete, documented reason it cannot run reliably in CI.
|
||||||
|
7. Run the test manually first, then through nextest once it is stable enough for the suite you chose.
|
||||||
|
|
||||||
|
## Writing the test body
|
||||||
|
|
||||||
|
The normal shape is:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use crate::Builder;
|
||||||
|
use warp::integration_testing::step::new_step_with_default_assertions;
|
||||||
|
use warp::integration_testing::terminal::{
|
||||||
|
clear_blocklist_to_remove_bootstrapped_blocks,
|
||||||
|
execute_command_for_single_terminal_in_tab,
|
||||||
|
wait_until_bootstrapped_single_pane_for_tab,
|
||||||
|
util::ExpectedExitStatus,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn test_example() -> Builder {
|
||||||
|
Builder::new()
|
||||||
|
.with_step(wait_until_bootstrapped_single_pane_for_tab(0))
|
||||||
|
.with_step(clear_blocklist_to_remove_bootstrapped_blocks())
|
||||||
|
.with_step(execute_command_for_single_terminal_in_tab(
|
||||||
|
0,
|
||||||
|
"echo hello".to_string(),
|
||||||
|
ExpectedExitStatus::Success,
|
||||||
|
"hello".to_string(),
|
||||||
|
))
|
||||||
|
.with_step(
|
||||||
|
new_step_with_default_assertions("Assert some UI state")
|
||||||
|
.add_named_assertion("specific assertion name", |app, window_id| {
|
||||||
|
// inspect app state and return AssertionOutcome
|
||||||
|
warpui::integration::AssertionOutcome::Success
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Prefer a small number of focused steps with descriptive names over a huge monolithic test.
|
||||||
|
|
||||||
|
## Builder guidance
|
||||||
|
|
||||||
|
### `Builder::new()`
|
||||||
|
|
||||||
|
Start here almost every time.
|
||||||
|
|
||||||
|
Warp's wrapper automatically gives you:
|
||||||
|
|
||||||
|
- a per-test root directory
|
||||||
|
- isolated `HOME`
|
||||||
|
- generated rc files for Bash, Zsh, and Fish
|
||||||
|
- file-backed user preferences
|
||||||
|
- a default 2-minute hard timeout
|
||||||
|
- real-display support if `WARPUI_USE_REAL_DISPLAY_IN_INTEGRATION_TESTS` is present
|
||||||
|
|
||||||
|
### `with_setup(...)`
|
||||||
|
|
||||||
|
Use this for filesystem or environment setup before the app runs.
|
||||||
|
|
||||||
|
Common patterns:
|
||||||
|
|
||||||
|
- `utils.set_env("NAME", Some(value))`
|
||||||
|
- creating files under `utils.test_dir()`
|
||||||
|
- writing fixture config files
|
||||||
|
|
||||||
|
Prefer this over reaching into the real filesystem.
|
||||||
|
|
||||||
|
### `with_user_defaults(...)`
|
||||||
|
|
||||||
|
Use this to set persisted Warp preferences before the test starts.
|
||||||
|
|
||||||
|
This is the right tool for settings backed by user preferences rather than environment variables.
|
||||||
|
|
||||||
|
### `set_should_run_test(...)`
|
||||||
|
|
||||||
|
Use this to gate tests on shell/platform/runtime capabilities when the test genuinely cannot run everywhere.
|
||||||
|
|
||||||
|
### `with_on_finish(...)`
|
||||||
|
|
||||||
|
Use this for final verification or artifact inspection that should happen after all steps complete, such as checking that screenshots or recordings were written.
|
||||||
|
|
||||||
|
### `with_real_display()`
|
||||||
|
|
||||||
|
Use this explicitly when the test needs a real display for frame capture or visual workflows. Video/screenshot tests should normally be manual or ignored in CI unless there is a stable real-display path.
|
||||||
|
|
||||||
|
## `TestStep` guidance
|
||||||
|
|
||||||
|
`TestStep` is the unit of execution. Each step can have:
|
||||||
|
|
||||||
|
- setup callbacks
|
||||||
|
- input events
|
||||||
|
- actions
|
||||||
|
- assertions
|
||||||
|
- a timeout
|
||||||
|
- retry count
|
||||||
|
- failure handling
|
||||||
|
|
||||||
|
### Start from helper constructors
|
||||||
|
|
||||||
|
Prefer:
|
||||||
|
|
||||||
|
- `wait_until_bootstrapped_single_pane_for_tab(0)`
|
||||||
|
- `new_step_with_default_assertions("...")`
|
||||||
|
- `new_step_with_default_assertions_for_pane("...", tab, pane)`
|
||||||
|
|
||||||
|
The default step helpers already assert:
|
||||||
|
|
||||||
|
- no pending model events
|
||||||
|
- no block executing
|
||||||
|
|
||||||
|
These are good baseline invariants for most UI interactions.
|
||||||
|
|
||||||
|
### Prefer helper APIs over raw event plumbing
|
||||||
|
|
||||||
|
Use high-level helpers from `app/src/integration_testing/` whenever possible:
|
||||||
|
|
||||||
|
- terminal command execution helpers
|
||||||
|
- block list helpers
|
||||||
|
- command palette helpers
|
||||||
|
- navigation helpers
|
||||||
|
- settings helpers
|
||||||
|
- workflow/file tree/notebook helpers
|
||||||
|
|
||||||
|
Drop to raw `with_event(...)`, `with_event_fn(...)`, or saved-position mouse events only when there is no suitable helper.
|
||||||
|
|
||||||
|
### Use named assertions
|
||||||
|
|
||||||
|
Prefer `add_named_assertion(...)` over unnamed assertions. Named assertions make failure output and runtime tags much easier to interpret.
|
||||||
|
|
||||||
|
### Use polling assertions instead of sleeps
|
||||||
|
|
||||||
|
Assertions are polled until success or timeout. Lean on that model instead of hardcoding sleeps.
|
||||||
|
|
||||||
|
Good pattern:
|
||||||
|
|
||||||
|
- trigger an event or action
|
||||||
|
- assert on the eventual UI/model state
|
||||||
|
|
||||||
|
Avoid brittle timing assumptions.
|
||||||
|
|
||||||
|
### Use step data when one step computes something for the next
|
||||||
|
|
||||||
|
If a later step needs data from an earlier one, use:
|
||||||
|
|
||||||
|
- `add_named_assertion_with_data_from_prior_step(...)`
|
||||||
|
- `StepDataMap`
|
||||||
|
|
||||||
|
This is useful for saving measured positions, counts, IDs, or other values from prior frames.
|
||||||
|
|
||||||
|
### Use retries sparingly
|
||||||
|
|
||||||
|
`set_retries(...)` can help for a legitimately retryable step, but do not use it to hide deterministic failures. Prefer making the step more robust first.
|
||||||
|
|
||||||
|
### Use `PreconditionFailed` for known environmental flakes
|
||||||
|
|
||||||
|
If the environment reaches a state where the rest of the test is invalid, return `AssertionOutcome::PreconditionFailed(...)` instead of failing hard. The outer harness can rerun the entire test up to 10 times.
|
||||||
|
|
||||||
|
The existing bootstrap helper is a good model for this.
|
||||||
|
|
||||||
|
## Common test-writing patterns
|
||||||
|
|
||||||
|
### 1. Wait for bootstrap first
|
||||||
|
|
||||||
|
For most terminal-facing tests, the first real step should be:
|
||||||
|
|
||||||
|
- `wait_until_bootstrapped_single_pane_for_tab(0)`
|
||||||
|
|
||||||
|
Do not start asserting on terminal UI before bootstrap completes.
|
||||||
|
|
||||||
|
### 2. Clear the bootstrapped blocks if block indices matter
|
||||||
|
|
||||||
|
If the test relies on saved positions like `block_index:0`, clear the block list after bootstrap:
|
||||||
|
|
||||||
|
- `clear_blocklist_to_remove_bootstrapped_blocks()`
|
||||||
|
|
||||||
|
Otherwise the first user-generated block index depends on bootstrap output and the active shell.
|
||||||
|
|
||||||
|
### 3. Use helper command runners
|
||||||
|
|
||||||
|
Prefer helpers like:
|
||||||
|
|
||||||
|
- `execute_command_for_single_terminal_in_tab(...)`
|
||||||
|
- `execute_echo(...)`
|
||||||
|
- `execute_echo_str(...)`
|
||||||
|
- `execute_long_running_command(...)`
|
||||||
|
|
||||||
|
These helpers already handle a lot of correctness and output validation.
|
||||||
|
|
||||||
|
### 4. Assert visible behavior, not just internal mutation
|
||||||
|
|
||||||
|
A good integration test verifies the user-observable behavior:
|
||||||
|
|
||||||
|
- output visible in the terminal
|
||||||
|
- focus moved where expected
|
||||||
|
- UI element opened/closed
|
||||||
|
- selection changed
|
||||||
|
- settings applied
|
||||||
|
|
||||||
|
Internal state assertions are still useful, but they should support the visible behavior rather than replace it.
|
||||||
|
|
||||||
|
### 5. Keep tests feature-focused
|
||||||
|
|
||||||
|
Write a test for one behavior or one closely related flow. If you need to cover multiple scenarios, consider multiple tests instead of one giant script.
|
||||||
|
|
||||||
|
## Running tests
|
||||||
|
|
||||||
|
### Run one test directly through the integration binary
|
||||||
|
|
||||||
|
Use this first while authoring:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo run -p integration --bin integration -- test_name
|
||||||
|
```
|
||||||
|
|
||||||
|
This is the fastest way to iterate on a specific test because it bypasses the outer Rust test wrapper and runs the named test directly.
|
||||||
|
|
||||||
|
### Run one test through nextest
|
||||||
|
|
||||||
|
Once it is wired into one of the `tests/integration/*.rs` macro lists, run it with nextest:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo nextest run --no-fail-fast --workspace test_name
|
||||||
|
```
|
||||||
|
|
||||||
|
### Run with a real display when needed
|
||||||
|
|
||||||
|
For screenshot/video or other real-display flows:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
WARPUI_USE_REAL_DISPLAY_IN_INTEGRATION_TESTS=1 cargo run -p integration --bin integration -- test_name
|
||||||
|
```
|
||||||
|
|
||||||
|
Or with nextest:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
WARPUI_USE_REAL_DISPLAY_IN_INTEGRATION_TESTS=1 cargo nextest run --no-fail-fast --workspace test_name
|
||||||
|
```
|
||||||
|
|
||||||
|
## Debugging and investigation
|
||||||
|
|
||||||
|
### Get a backtrace on failures
|
||||||
|
|
||||||
|
```bash
|
||||||
|
RUST_BACKTRACE=1 cargo run -p integration --bin integration -- test_name
|
||||||
|
```
|
||||||
|
|
||||||
|
### Pause on failure
|
||||||
|
|
||||||
|
This is useful when running locally and you want to inspect the failed UI state:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
WARPUI_PAUSE_INTEGRATION_TEST_ON_FAILURE=1 cargo run -p integration --bin integration -- test_name
|
||||||
|
```
|
||||||
|
|
||||||
|
### Pause after every step
|
||||||
|
|
||||||
|
Useful for understanding exactly what the test is doing:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
WARPUI_PAUSE_INTEGRATION_TEST_AT_EVERY_STEP=1 cargo run -p integration --bin integration -- test_name
|
||||||
|
```
|
||||||
|
|
||||||
|
### Video and screenshots
|
||||||
|
|
||||||
|
If the task is specifically about recording a test, collecting screenshots, or validating overlay/video artifacts, also use the `integration-test-video` skill (located at `.warp/skills/integration-test-video/SKILL.md`).
|
||||||
|
|
||||||
|
### Environment variable gotcha
|
||||||
|
|
||||||
|
`utils.set_env(...)` affects runtime environment lookups such as `std::env::var(...)`.
|
||||||
|
|
||||||
|
It does not affect compile-time lookups like `option_env!(...)`. If the product code uses `option_env!`, changing the env var inside the test will not change that behavior without rebuilding.
|
||||||
|
|
||||||
|
## Verification checklist
|
||||||
|
|
||||||
|
Before considering a new integration test done, verify all of the following:
|
||||||
|
|
||||||
|
- The test function lives under `crates/integration/src/test/`.
|
||||||
|
- The module is added and re-exported in `crates/integration/src/test.rs`.
|
||||||
|
- The test is registered in `crates/integration/src/bin/integration.rs`.
|
||||||
|
- The test is listed in the correct nextest macro file and will run in CI by default, unless it was explicitly made manual-only with a documented reason.
|
||||||
|
- The test passes when run directly through the integration binary.
|
||||||
|
- The test passes through nextest if it is meant to be part of the automated suite.
|
||||||
|
- The assertions check the intended user-visible behavior.
|
||||||
|
- The test does not depend on the developer's real home directory, shell config, or machine state.
|
||||||
|
- If the test uses screenshots/video, the produced artifacts were actually inspected rather than only assuming they exist.
|
||||||
|
|
||||||
|
## Anti-patterns to avoid
|
||||||
|
|
||||||
|
- Writing a test only in `src/test/*.rs` and forgetting the nextest macro list.
|
||||||
|
- Asserting on bootstrap-sensitive block indices without clearing the bootstrapped blocks first.
|
||||||
|
- Using raw events everywhere when a helper already exists.
|
||||||
|
- Adding sleeps instead of assertion polling.
|
||||||
|
- Making the test depend on personal dotfiles, real settings, or non-hermetic filesystem state.
|
||||||
|
- Using retries to paper over a deterministic bug.
|
||||||
|
- Leaving a real-display/manual test enabled in CI without a stable path.
|
||||||
|
|
||||||
|
## Good workflow for agents
|
||||||
|
|
||||||
|
When asked to add or fix an integration test:
|
||||||
|
|
||||||
|
1. Find the closest existing integration test module for the feature.
|
||||||
|
2. Reuse helper assertions and step constructors before inventing new low-level plumbing.
|
||||||
|
3. Register the test in all required places, not just the implementation file.
|
||||||
|
4. Run the test manually first.
|
||||||
|
5. If it belongs in automation, run it with nextest too.
|
||||||
|
6. If the test exercises visual behavior, verify the resulting UI behavior or artifacts directly.
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
---
|
||||||
|
name: warp-ui-guidelines
|
||||||
|
description: Catalog of guidelines for writing UI code in the Warp client. Read whenever doing any UI work in this repo, up front before writing the change, so the relevant guidelines shape the implementation.
|
||||||
|
---
|
||||||
|
|
||||||
|
# warp-ui-guidelines
|
||||||
|
|
||||||
|
This skill is a growing catalog of guidelines for working on Warp's UI code. Each guideline captures a lesson that would otherwise be re-learned through review — typically because an agent or contributor reinvented a component, drifted from the design system, or bypassed a shared abstraction.
|
||||||
|
|
||||||
|
**How to use this skill:**
|
||||||
|
|
||||||
|
- Read through the guidelines below once at the start of any UI task, then keep them in mind while implementing. The list is short enough to scan.
|
||||||
|
- Each guideline is self-contained. Not every one will apply to every task — use judgment. But if a guideline *does* apply, follow it.
|
||||||
|
- When in doubt, prefer reusing an existing abstraction over introducing a new one. The Warp UI has accumulated a well-factored set of shared components and themes; new one-offs almost always drift.
|
||||||
|
|
||||||
|
New guidelines get added here over time. If you discover a recurring UI mistake that would have been caught by a written rule, add it.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Guideline: Reuse button themes
|
||||||
|
|
||||||
|
Button colors come from a shared set of `ActionButtonTheme` impls in `app/src/view_components/action_button.rs` (and the parallel `Theme` impls in `crates/ui_components/src/button/themes.rs`) — `PrimaryTheme`, `SecondaryTheme`, `NakedTheme`, `DangerPrimaryTheme`, etc. These encode the design system and keep button colors consistent across the app.
|
||||||
|
|
||||||
|
When styling a button, **use one of the existing themes unchanged**. The shared themes are well-established and vetted; if one looks "wrong" for your use case, the most likely explanation is that you're reaching for the wrong theme, not that the theme is buggy.
|
||||||
|
|
||||||
|
Do **not** modify a shared theme on your own initiative. Changing `PrimaryTheme`, `SecondaryTheme`, etc. affects every button in the app, and a tweak that fixes your screen can silently regress others. Only edit a shared theme when the user has explicitly confirmed that the design-system component itself needs to change.
|
||||||
|
|
||||||
|
Red flags that you're about to make buttons inconsistent:
|
||||||
|
|
||||||
|
- Writing a new `impl ActionButtonTheme for FooPrimaryTheme` that delegates to `PrimaryTheme` and only tweaks one method (usually `text_color`). Almost always the right move is to use `PrimaryTheme` directly and accept the result.
|
||||||
|
- Hard-coding `ColorU::new(...)` instead of using `appearance.theme()` accessors (`accent`, `font_color(bg)`, `foreground`, etc.).
|
||||||
|
- Setting `should_opt_out_of_contrast_adjustment` to `true` to force a specific label color.
|
||||||
|
- Naming a theme after a feature or view (`FooPrimaryTheme`, `BarSubmitTheme`) rather than a design-system role.
|
||||||
|
|
||||||
|
If an existing theme genuinely doesn't fit and you think the shared theme should change, surface that to the user before editing it, rather than either editing it unilaterally or papering over it with a one-off.
|
||||||
@@ -0,0 +1,164 @@
|
|||||||
|
---
|
||||||
|
name: write-product-spec
|
||||||
|
description: Write a PRODUCT.md spec for a significant user-facing feature in Warp, focused on detailed behavior and validation. Use when the user asks for a product spec, desired behavior doc, or PRD, wants to define feature behavior before implementation, or when the feature is substantial or behaviorally ambiguous enough that a written spec would improve implementation or review.
|
||||||
|
---
|
||||||
|
|
||||||
|
# write-product-spec
|
||||||
|
|
||||||
|
Write a `PRODUCT.md` spec for a significant feature in Warp.
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
The product spec should make the desired behavior unambiguous enough that an agent can implement it correctly and avoid regressions. Describe the feature purely from the user's perspective — what the user sees, does, and experiences, and the invariants that must hold for them. Do not include implementation details (internal types, state layout, module boundaries, data flow, algorithms).
|
||||||
|
|
||||||
|
"User" is not limited to the end user of the Warp app. It means whoever consumes the surface being designed:
|
||||||
|
|
||||||
|
- For UI / UX features: the human using Warp.
|
||||||
|
- For a data model: the code that reads and writes that model.
|
||||||
|
- For an API, protocol, or library: the callers of that API — other services, client code, plugins, or agents.
|
||||||
|
- For a CLI tool or developer-facing surface: the developer invoking it.
|
||||||
|
|
||||||
|
The spec should describe behavior from that consumer's perspective: the shape of the surface, the operations they can perform, what they see back, invariants they can rely on, and edge cases they must handle — without prescribing how the surface is implemented underneath.
|
||||||
|
|
||||||
|
Implementation details, validation, and test planning live in a companion `TECH.md`, produced by the `write-tech-spec` skill. Writing the product spec is usually the first step of a two-step process: once `PRODUCT.md` is agreed on, invoke `write-tech-spec` to produce `TECH.md` for the same feature (or let the user know that's the expected next step). The product spec should be written so the tech spec can be written directly from it.
|
||||||
|
|
||||||
|
Write specs to `specs/<id>/PRODUCT.md`, where `<id>` is one of:
|
||||||
|
|
||||||
|
- a Linear ticket number (e.g. `specs/APP-1234/PRODUCT.md`)
|
||||||
|
- a GitHub issue id, prefixed with `gh-` (e.g. `specs/gh-4567/PRODUCT.md`)
|
||||||
|
- a short kebab-case feature name (e.g. `specs/vertical-tabs-hover-sidecar/PRODUCT.md`)
|
||||||
|
|
||||||
|
`specs/` should contain only id-named directories as direct children — no engineer-named subdirectories.
|
||||||
|
|
||||||
|
Ticket / issue references are optional. If the user has a Linear ticket or GitHub issue, use its id. If they don't, ask them for a feature name to use as the directory. Only create a new Linear ticket or GitHub issue when the user explicitly asks for one; in that case use the Linear MCP tools or `gh` CLI respectively (and `ask_user_question` if team, labels, or repo are unclear).
|
||||||
|
|
||||||
|
## Before writing
|
||||||
|
|
||||||
|
Gather only the context you need: directory id (Linear ticket, GitHub issue, or feature name), feature summary, target users, key behaviors, edge cases, and how the feature will be validated. Use `ask_user_question` for missing context rather than guessing.
|
||||||
|
|
||||||
|
### Figma mocks
|
||||||
|
|
||||||
|
If the feature has any UI or interaction design, ask the user whether a Figma mock exists before drafting the Behavior section, and include the link in the spec when one is provided. A mock is often the most reliable source of truth for visual states, spacing, and edge-case layouts — not asking can cause the Behavior section to guess at intent the designer already settled.
|
||||||
|
|
||||||
|
- If the user provides a link, include it under a short `## Figma` section (or inline near the top of Behavior) as `Figma: <link>`.
|
||||||
|
- If the user confirms no mock exists, note `Figma: none provided` so the absence is explicit rather than ambiguous.
|
||||||
|
- If the feature is purely backend (data model, API, CLI with no visual surface), skip the question and omit the section.
|
||||||
|
|
||||||
|
Do not silently drop design context; an explicit "none" is preferable to no mention at all on features where design would normally be expected.
|
||||||
|
|
||||||
|
## Structure
|
||||||
|
|
||||||
|
Required sections:
|
||||||
|
|
||||||
|
1. **Summary** — 1–3 sentences describing the feature and desired outcome.
|
||||||
|
2. **Behavior** — The meat of the spec. An exhaustive English description of how the feature works, written as numbered, testable invariants. See "The Behavior section" below — this is where the spec earns its length, and everything else should stay thin to avoid duplicating it.
|
||||||
|
|
||||||
|
Optional sections — include only when they add signal beyond the core. Omit the heading entirely if empty; do not write "None" as a placeholder.
|
||||||
|
|
||||||
|
- **Problem** — Include only when the motivation isn't obvious from Summary.
|
||||||
|
- **Goals / Non-goals** — Include when scope is ambiguous or has been contested.
|
||||||
|
- **Figma** — Include with a link when one exists, or an explicit `Figma: none provided` note when design matters but no mock exists. Omit entirely for non-visual features. See "Figma mocks" above.
|
||||||
|
- **Open questions** — Prefer inline `**Open question:** …` next to the relevant behavior. Include a dedicated section only if there are multiple unresolved questions worth collecting.
|
||||||
|
|
||||||
|
Do not include Validation, Success criteria, or Testing sections. Validation and test planning live in the companion `TECH.md` (produced by `write-tech-spec`). Write Behavior as numbered invariants that are testable on their own — the tech spec can reference them directly.
|
||||||
|
|
||||||
|
## The Behavior section
|
||||||
|
|
||||||
|
Behavior is the spec. Everything else is framing.
|
||||||
|
|
||||||
|
The goal of Behavior is a complete English description of how the feature works, detailed enough that a tech spec can be written directly from it without the author having to guess or re-derive product intent. If a reader finishes Behavior with questions about what the feature does in some situation, the section is not done.
|
||||||
|
|
||||||
|
Describe, at minimum:
|
||||||
|
|
||||||
|
- Default behavior and the happy-path user flow.
|
||||||
|
- Every user-visible state and the transitions between them.
|
||||||
|
- All inputs the user can provide and how the feature responds.
|
||||||
|
- Empty states, error states, loading / pending states, and cancellation.
|
||||||
|
- Edge cases a reasonable implementer would not think to ask about — permission denied, offline, timeouts, races between state changes, multiple concurrent instances, stale or missing data, focus loss mid-interaction, interactions with adjacent features.
|
||||||
|
- Keyboard, accessibility, and focus expectations where relevant.
|
||||||
|
- Invariants that must hold at all times and behaviors that must not regress.
|
||||||
|
|
||||||
|
Length Behavior to match the feature. Trivial features may need a handful of invariants; complex features may need many, with sub-sections per flow or state. The rest of the spec should stay thin so Behavior can be as exhaustive as the feature requires without producing a bloated document overall. Err toward enumerating one more edge case rather than one fewer.
|
||||||
|
|
||||||
|
## Length heuristic
|
||||||
|
|
||||||
|
Behavior should be as long as the feature requires — do not truncate edge cases to hit a line target. The heuristic below applies to everything around Behavior (Summary, optional sections): keep that framing thin so the spec's total length reflects the feature's actual complexity, not structural overhead.
|
||||||
|
|
||||||
|
- Trivial fix or narrow UI tweak: no spec.
|
||||||
|
- Small feature (single module, few edge cases): framing plus Behavior typically ~30–60 lines total.
|
||||||
|
- Medium feature (cross-module, multiple states): typically ~80–150 lines total.
|
||||||
|
- Large or behaviorally rich feature: longer is fine, and most of the length should live in Behavior.
|
||||||
|
|
||||||
|
If you find yourself writing the same idea in Summary, Problem, Goals, and Behavior, collapse the framing — not the Behavior content.
|
||||||
|
|
||||||
|
## Writing guidance
|
||||||
|
|
||||||
|
- Prefer concrete, observable behavior over aspirational wording.
|
||||||
|
- Write Behavior as a list of invariants rather than prose when possible.
|
||||||
|
- Capture invariants that must not regress and edge cases that are easy to miss.
|
||||||
|
- Avoid implementation details unless unavoidable for the UX.
|
||||||
|
- Each section should earn its place — if a section would repeat another or contain only boilerplate, omit it.
|
||||||
|
|
||||||
|
## Keep the spec current
|
||||||
|
|
||||||
|
Approved specs may ship in the same PR as the implementation. As implementation evolves, update `PRODUCT.md` in the same PR when user-facing behavior or UX details change. The checked-in spec should describe the feature that actually ships.
|
||||||
|
|
||||||
|
For large features, the implementer may optionally keep a `DECISIONS.md` file summarizing concrete decisions made during design and implementation. Offer it when it would help future agents; otherwise skip it.
|
||||||
|
|
||||||
|
## Related Skills
|
||||||
|
|
||||||
|
- `implement-specs`
|
||||||
|
- `write-tech-spec`
|
||||||
|
- `spec-driven-implementation`
|
||||||
|
|
||||||
|
## Example Behavior section
|
||||||
|
|
||||||
|
A sample Behavior section for a hypothetical feature: rendering GitHub-flavored Markdown tables in the Warp block list. It demonstrates the expected shape — numbered, testable, user-perspective invariants that enumerate defaults, edge cases, malformed input, streaming, selection/copy, search, sharing, theming, and cross-surface consistency, with one inline open question.
|
||||||
|
|
||||||
|
````markdown
|
||||||
|
## Behavior
|
||||||
|
|
||||||
|
1. When a terminal output block contains a GitHub-flavored Markdown table (a header row, a separator row of one or more `---` segments, and one or more body rows, all delimited by `|`), that table renders as a visually formatted table in the block — not as raw pipe-delimited text.
|
||||||
|
|
||||||
|
2. The table renders with:
|
||||||
|
- A visually distinct header row.
|
||||||
|
- Aligned columns based on the separator row: `|:---|` left-align, `|:---:|` center, `|---:|` right-align. `|---|` with no colons falls back to the default alignment (left for text, right for numeric-looking values).
|
||||||
|
- Visible row separators (or equivalent spacing) consistent with the active theme.
|
||||||
|
|
||||||
|
3. Inline markdown inside a cell renders inline: bold, italic, inline code, strikethrough, and links all render the same way they do in the surrounding block output. Line breaks inside a cell (`<br>` or escaped `\n`) render as in-cell line breaks.
|
||||||
|
|
||||||
|
4. Column widths are chosen to fit the table's natural content when it fits inside the block. If a single cell's content is very long, that cell wraps its text within its column rather than forcing the column to an unreasonable width.
|
||||||
|
- **Open question:** when a wrapped cell would produce an unreasonably tall row, do we clip with an "expand" affordance, or let the row grow unbounded?
|
||||||
|
|
||||||
|
5. Horizontal scrolling: when the table's total width exceeds the block width — many columns, or wide columns that can't reasonably be narrowed — the table becomes horizontally scrollable within the block. Scrolling horizontally reveals off-screen columns without clipping or truncating them. Vertical scrolling of the block continues to work independently of table scroll.
|
||||||
|
|
||||||
|
6. When the block is resized (terminal resize, pane split, sidebar open/close), the table reflows to the new width without losing row or column order.
|
||||||
|
|
||||||
|
7. Empty cells render as visibly empty (same row height as surrounding cells, no placeholder text). A row with all empty cells still renders as a row.
|
||||||
|
|
||||||
|
8. A table with only a header and separator (zero body rows) renders as a header-only table, not as raw text.
|
||||||
|
|
||||||
|
9. A single-column table renders as a single-column table (not collapsed to a bullet list or similar).
|
||||||
|
|
||||||
|
10. Malformed tables fall back gracefully:
|
||||||
|
- Missing separator row → rendered as preformatted text, not as a table.
|
||||||
|
- Ragged rows (some rows have fewer or more cells than the header) → missing cells render empty; extra cells are shown, with the header row extended visually if possible. The block should never silently drop data.
|
||||||
|
- Unclosed table (last row truncated mid-stream) → rendered as a partial table; see (11).
|
||||||
|
|
||||||
|
11. Streaming output: while a command is still producing rows, the table renders incrementally. New rows append as they arrive. The header row locks in as soon as the separator line is received; rows before the separator render as plain text until the table is recognized.
|
||||||
|
|
||||||
|
12. Selection and copy:
|
||||||
|
- Selecting across cells with the mouse or keyboard selects their visible text content.
|
||||||
|
- Copying the selection produces tab-separated plain text by default (one row per line, cells separated by tabs). An affordance (context menu, shortcut) lets the user copy the original markdown source instead.
|
||||||
|
- Copying the entire block preserves the original markdown source verbatim.
|
||||||
|
|
||||||
|
13. Search within a block (find-in-block) matches against cell text content. Matches highlight in place in the rendered cell; navigating matches scrolls the table into view, including horizontally if the match is in an off-screen column.
|
||||||
|
|
||||||
|
14. Sharing or exporting a block (Warp Drive, share link, save as file) preserves the original markdown source, not the rendered form.
|
||||||
|
|
||||||
|
15. Theming: table borders, header backgrounds, alternating row shading (if any), and link/code styles all come from the active Warp theme. No hard-coded colors.
|
||||||
|
|
||||||
|
16. Markdown tables render consistently wherever block-list markdown already renders — command output, agent responses, and any other block type that supports inline markdown. The same input produces the same table in each surface.
|
||||||
|
|
||||||
|
17. Non-table pipe content is not misrendered as a table. Text that contains `|` characters but no valid header-separator line remains plain text, even if it visually resembles a table.
|
||||||
|
````
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
---
|
||||||
|
name: write-tech-spec
|
||||||
|
description: Write a TECH.md spec for a significant Warp feature after researching the current codebase and implementation constraints. Use when the user asks for a technical spec, implementation plan, or architecture doc tied to a product spec.
|
||||||
|
---
|
||||||
|
|
||||||
|
# write-tech-spec
|
||||||
|
|
||||||
|
Write a `TECH.md` spec for a significant feature in Warp.
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
The tech spec should translate product intent into an implementation plan that fits the existing codebase, documents architectural choices, and makes the work easier for agents to execute and reviewers to evaluate.
|
||||||
|
|
||||||
|
Write specs to `specs/<id>/TECH.md`, where `<id>` is one of:
|
||||||
|
|
||||||
|
- a Linear ticket number (e.g. `specs/APP-1234/TECH.md`)
|
||||||
|
- a GitHub issue id, prefixed with `gh-` (e.g. `specs/gh-4567/TECH.md`)
|
||||||
|
- a short kebab-case feature name (e.g. `specs/vertical-tabs-hover-sidecar/TECH.md`)
|
||||||
|
|
||||||
|
Match the id used by the sibling `PRODUCT.md` when one exists. `specs/` should contain only id-named directories as direct children.
|
||||||
|
|
||||||
|
Ticket / issue references are optional. If the user has a Linear ticket or GitHub issue, use its id. If they don't, ask them for a feature name to use as the directory. Only create a new Linear ticket or GitHub issue when the user explicitly asks for one; in that case use the Linear MCP tools or `gh` CLI respectively (and `ask_user_question` if team, labels, or repo are unclear).
|
||||||
|
|
||||||
|
## When to use
|
||||||
|
|
||||||
|
Use this skill when the implementation spans multiple modules, has meaningful architectural tradeoffs, or when reviewers will benefit from seeing the plan before or alongside the code. For pure UI changes or straightforward fixes, a tech spec is often unnecessary.
|
||||||
|
|
||||||
|
Prefer to have a `PRODUCT.md` first so the technical plan is anchored to agreed behavior. If the implementation is still too uncertain, build an e2e prototype first and then write the tech spec from what was learned.
|
||||||
|
|
||||||
|
## Research before writing
|
||||||
|
|
||||||
|
Before drafting, read the product spec (if any), inspect the relevant code, and identify the main files, types, data flow, and ownership boundaries. Do not guess about current architecture when the code can be inspected directly.
|
||||||
|
|
||||||
|
## Structure
|
||||||
|
|
||||||
|
Required sections:
|
||||||
|
|
||||||
|
1. **Context** — What's being built, how the current system works in the area being changed, and the most relevant files with line references. Combine the "problem," "current state," and "relevant code" into one grounded section. Example references:
|
||||||
|
- `app/src/workspace/mod.rs:42` — entry point for the user flow
|
||||||
|
- `app/src/workspace/workspace.rs (120-220)` — state and event handling that will likely change
|
||||||
|
Reference `PRODUCT.md` for user-visible behavior rather than restating it.
|
||||||
|
2. **Proposed changes** — The implementation plan: which modules change, new types/APIs/state being introduced, data flow, ownership boundaries, and how the design follows existing patterns. Call out tradeoffs when there is more than one reasonable path.
|
||||||
|
3. **Testing and validation** — How the implementation will be verified against the product behavior. Owns everything about proving the feature works: unit tests, integration tests, manual steps, screenshots, videos, and any other verification. Reference the numbered Behavior invariants from `PRODUCT.md` directly rather than restating them; each important invariant should map to a concrete test or verification step. This section is where validation lives — `PRODUCT.md` intentionally does not have a Validation section.
|
||||||
|
|
||||||
|
Optional sections — include only when they add signal. Omit the heading entirely if empty; do not write "None" as a placeholder.
|
||||||
|
|
||||||
|
- **End-to-end flow** — Include only when tracing the path through the system tells you something the Proposed changes list doesn't.
|
||||||
|
- **Diagram** — Include a Mermaid diagram only when a visual will explain the design faster than prose (data flow, state transitions, sequence across layers). Prefer one or two focused diagrams over decorative ones.
|
||||||
|
- **Risks and mitigations** — Include when there are real failure modes, regressions, migration concerns, or rollout hazards worth calling out.
|
||||||
|
- **Parallelization** — Include when work can cleanly split across multiple agents and that split is non-obvious.
|
||||||
|
- **Follow-ups** — Include when there is deferred cleanup or future work worth naming.
|
||||||
|
|
||||||
|
## Length heuristic
|
||||||
|
|
||||||
|
Right-size the spec to the feature:
|
||||||
|
|
||||||
|
- Single-file change with clear approach: skip the tech spec or keep it under ~40 lines.
|
||||||
|
- Multi-module change with some ambiguity: target ~80–150 lines.
|
||||||
|
- Large cross-cutting or architecturally novel change: longer is fine when every section earns its place.
|
||||||
|
|
||||||
|
If Context and Proposed changes end up describing the same files and state from different angles, collapse them.
|
||||||
|
|
||||||
|
## Writing guidance
|
||||||
|
|
||||||
|
- Ground the plan in actual codebase structure and patterns.
|
||||||
|
- Prefer concrete implementation guidance over generic architecture language.
|
||||||
|
- Explain why the proposed design fits this repo.
|
||||||
|
- Reference `PRODUCT.md` for behavior instead of restating it.
|
||||||
|
- Each section should earn its place — if a section would repeat another or contain only boilerplate, omit it.
|
||||||
|
|
||||||
|
## Keep the spec current
|
||||||
|
|
||||||
|
Approved specs may ship in the same PR as the implementation. Update `TECH.md` in the same PR when module boundaries, implementation sequencing, risks, validation strategy, or rollout assumptions change. The checked-in spec should describe the implementation that actually ships.
|
||||||
|
|
||||||
|
For large features, the implementer may optionally keep a `DECISIONS.md` file summarizing concrete decisions. Offer it when it would help future agents; otherwise skip it.
|
||||||
|
|
||||||
|
## Related Skills
|
||||||
|
|
||||||
|
- `implement-specs`
|
||||||
|
- `write-product-spec`
|
||||||
|
- `spec-driven-implementation`
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
[env]
|
||||||
|
# This is used to set the minimum deployment target for mac
|
||||||
|
# https://cmake.org/cmake/help/latest/envvar/MACOSX_DEPLOYMENT_TARGET.html
|
||||||
|
# If unset, it uses the system one, but we want to build for older versions
|
||||||
|
# of mac os by default.
|
||||||
|
#
|
||||||
|
# This should be kept in sync with the definition of this variable in
|
||||||
|
# `script/bundle`.
|
||||||
|
MACOSX_DEPLOYMENT_TARGET = "10.14"
|
||||||
|
|
||||||
|
[build]
|
||||||
|
rustflags = ["-C", "symbol-mangling-version=v0", "-C", "link-args=-Wl,-headerpad_max_install_names"]
|
||||||
|
|
||||||
|
[net]
|
||||||
|
git-fetch-with-cli = true
|
||||||
|
|
||||||
|
[target.'cfg(target_family = "wasm")']
|
||||||
|
rustflags = ["--cfg=web_sys_unstable_apis"]
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{}
|
||||||
Symlink
+1
@@ -0,0 +1 @@
|
|||||||
|
../.agents/skills
|
||||||
Executable
+23
@@ -0,0 +1,23 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Read JSON input from stdin
|
||||||
|
input=$(cat)
|
||||||
|
|
||||||
|
# Extract model display name
|
||||||
|
MODEL=$(echo "$input" | jq -r '.model.display_name // "Claude"')
|
||||||
|
|
||||||
|
# Extract current working directory (just the folder name)
|
||||||
|
CURRENT_DIR=$(echo "$input" | jq -r '.workspace.current_dir // "~"')
|
||||||
|
DIR_NAME="${CURRENT_DIR##*/}"
|
||||||
|
|
||||||
|
# Get git branch if in a git repository
|
||||||
|
GIT_BRANCH=""
|
||||||
|
if git rev-parse --git-dir > /dev/null 2>&1; then
|
||||||
|
BRANCH=$(git branch --show-current 2>/dev/null)
|
||||||
|
if [ -n "$BRANCH" ]; then
|
||||||
|
GIT_BRANCH=" | Branch: $BRANCH"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Output formatted status line
|
||||||
|
echo "[$MODEL] 📁 $DIR_NAME$GIT_BRANCH"
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
disallowed-macros = [
|
||||||
|
{ path = "std::dbg", reason = "dbg!() is only for use in local testing, not submitted code" },
|
||||||
|
]
|
||||||
|
|
||||||
|
disallowed-types = [
|
||||||
|
{ path = "std::time::Instant", reason = "std::time::Instant is not implemented for wasm targets. Use instant::Instant instead." },
|
||||||
|
{ path = "std::process::Command", reason = "std::process::Command by default flashes a terminal when invoked on Windows. Use command::blocking::Command instead." },
|
||||||
|
{ path = "async_process::Command", reason = "async_process::Command by default flashes a terminal when invoked on Windows. Use command::r#async::Command instead." },
|
||||||
|
]
|
||||||
|
|
||||||
|
disallowed-methods = [
|
||||||
|
{ path = "async_channel::Sender::send_blocking", reason = "send_blocking() does not exist for wasm. Use warpui::r#async::block_on() with send() instead.", allow-invalid = true },
|
||||||
|
{ path = "line_ending::LineEnding::from_current_platform", reason = "line_ending::LineEnding::from_current_platform does not account for Unix-like subsystems for Windows. In most cases, use warp_core::platform::SessionPlatform::default_line_ending() instead." },
|
||||||
|
]
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
[profile.default]
|
||||||
|
# Mark a test as slow if it takes longer than 30s to execute, and terminate it
|
||||||
|
# if it takes 2x as long as the slow timeout (i.e.: 60s total).
|
||||||
|
slow-timeout = { period = "30s", terminate-after = 2 }
|
||||||
|
|
||||||
|
[[profile.default.overrides]]
|
||||||
|
filter = 'package(integration)'
|
||||||
|
# Integration tests are heavier-weight than unit tests, so have each one
|
||||||
|
# count twice against the parallelism limit. This improves overall runtime
|
||||||
|
# of the test suite, despite running fewer integration tests at a time.
|
||||||
|
threads-required = 2
|
||||||
|
|
||||||
|
[profile.ci]
|
||||||
|
# Print out output for failing tests as soon as they fail, and also at the end
|
||||||
|
# of the run (for easy scrollability).
|
||||||
|
failure-output = "immediate-final"
|
||||||
|
# Do not cancel the test run on the first failure.
|
||||||
|
fail-fast = false
|
||||||
|
|
||||||
|
[[profile.ci.overrides]]
|
||||||
|
filter = 'package(integration)'
|
||||||
|
# On CI, try integration tests up to 3 times (in total) to reduce the impact of
|
||||||
|
# flaky tests.
|
||||||
|
retries = 2
|
||||||
|
|
||||||
|
[profile.ci.junit]
|
||||||
|
# Output nextest results in junit format (for uploading to buildpulse).
|
||||||
|
# If `--profile ci` is selected on the command line, then the JUnit report will
|
||||||
|
# be written out to `target/nextest/ci/junit.xml`.
|
||||||
|
# Docs: https://nexte.st/docs/machine-readable/junit/
|
||||||
|
path = "junit.xml"
|
||||||
|
|
||||||
|
[[profile.ci.overrides]]
|
||||||
|
# On Windows CI, update_manager tests are flaky and often time out but succeed after a retry
|
||||||
|
# Evidence suggest that the test itself passes but the process running it hangs
|
||||||
|
# We haven't figured out why the process hangs, so this is a workaround to unblock devs
|
||||||
|
filter = 'test(update_manager)'
|
||||||
|
platform = { host = 'cfg(target_os = "windows")' }
|
||||||
|
retries = 2
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
# Don't include the git repository.
|
||||||
|
.git
|
||||||
|
|
||||||
|
app/Carthage
|
||||||
|
app/frameworks/default/Carthage
|
||||||
|
app/frameworks/dev/Carthage
|
||||||
|
/target
|
||||||
|
/app/target
|
||||||
|
/warp.xcworkspace
|
||||||
|
.idea/
|
||||||
|
.DS_Store
|
||||||
|
*.icloud
|
||||||
|
app/src/server/graphql/schema/generated
|
||||||
|
crates/command-signatures-v2/js/build
|
||||||
|
crates/command-signatures-v2/js/node_modules
|
||||||
|
|
||||||
|
# For testing changes to the channel versions file
|
||||||
|
channel_versions_test.json
|
||||||
|
|
||||||
|
# Don't include any CPU profiling output by accident.
|
||||||
|
profile.pb
|
||||||
|
|
||||||
|
# Don't include any files that we write for testing purposes.
|
||||||
|
crates/warp_files/test_data/test_write
|
||||||
|
|
||||||
|
# Don't include fonts downloaded by the script used to generate font fallback code.
|
||||||
|
script/font_fallback/downloaded_fonts
|
||||||
|
|
||||||
|
# Don't include the generated Windows installer
|
||||||
|
script/windows/Output
|
||||||
|
.aider*
|
||||||
|
|
||||||
|
# temporary vim files. Source: https://github.com/github/gitignore/blob/main/Global/Vim.gitignore
|
||||||
|
*~
|
||||||
|
[._]*.s[a-v][a-z]
|
||||||
|
!*.svg # keep svg files
|
||||||
|
[._]*.sw[a-p]
|
||||||
|
[._]s[a-rt-v][a-z]
|
||||||
|
[._]ss[a-gi-z]
|
||||||
|
[._]sw[a-p]
|
||||||
|
|
||||||
|
# Don't include the PTY recording.
|
||||||
|
warp.pty.recording
|
||||||
|
|
||||||
|
# Ignore all Dockerfiles. A change in the Dockerfile would otherwise be
|
||||||
|
# considered a change in the source code requiring a rebuild.
|
||||||
|
**/Dockerfile
|
||||||
|
|
||||||
|
# Don't include CI configuration
|
||||||
|
.github/
|
||||||
|
|
||||||
|
# Don't include the warp-server checkout in SWE-bench runs.
|
||||||
|
warp-server/
|
||||||
|
|
||||||
|
# Ignore large directories that aren't required for any current Dockerfiles.
|
||||||
|
app/assets/windows
|
||||||
|
app/src/terminal/ref_tests
|
||||||
|
|
||||||
|
# Ignore files only required for tests
|
||||||
|
**/*.sqlite
|
||||||
|
|
||||||
|
# Ignore yarn caches.
|
||||||
|
**/.yarn
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
*.pdb filter=lfs diff=lfs merge=lfs -text
|
||||||
|
input_classifier/models/* filter=lfs diff=lfs merge=lfs -text
|
||||||
|
input_classifier/models/**/*tokenizer.json linguist-generated=true
|
||||||
@@ -0,0 +1,137 @@
|
|||||||
|
name: Bug Report
|
||||||
|
description: "Found a bug? Please search through our open issues and docs, to make sure it isn't already submitted. If you have an SSH related issue, please use the SSH Template below"
|
||||||
|
labels: ["bug", "ready-to-implement"]
|
||||||
|
body:
|
||||||
|
- type: checkboxes
|
||||||
|
attributes:
|
||||||
|
label: "Pre-submit Checks"
|
||||||
|
options:
|
||||||
|
- label: "I have [searched Warp bugs](https://github.com/warpdotdev/warp/issues?q=is%3Aissue+label%3ABUG) and there are no duplicates"
|
||||||
|
required: true
|
||||||
|
- label: "I have [searched Warp known issues page](https://docs.warp.dev/help/known-issues) and my issue is not there"
|
||||||
|
required: true
|
||||||
|
- label: "I have an issue with AI and have included the debugging ID (Optional, but helps expedite the AI quality fix). [Debugging ID instructions](https://docs.warp.dev/agent-platform/agent/ai-faqs#gathering-ai-debugging-id)"
|
||||||
|
required: false
|
||||||
|
- label: "I have technical issue and have included the logs (optional, but helps expedite the bug fix). [Log instructions](https://docs.warp.dev/support-and-community/troubleshooting-and-support/sending-us-feedback#gathering-warp-logs)"
|
||||||
|
required: false
|
||||||
|
- type: textarea
|
||||||
|
id: "describe-the-bug"
|
||||||
|
attributes:
|
||||||
|
label: "Describe the bug"
|
||||||
|
description: "A clear and concise description of what the bug is."
|
||||||
|
placeholder: Tell us what you see.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: "to-reproduce"
|
||||||
|
attributes:
|
||||||
|
label: "To reproduce"
|
||||||
|
description: "Bug reports with clear reproduction will get prioritized higher and addressed more quickly."
|
||||||
|
placeholder: "Steps to reproduce: 1. Go to '...' 2. Click on '...' 3. Scroll down to '...' 4. See error '...'"
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: "expected-behavior"
|
||||||
|
attributes:
|
||||||
|
label: "Expected behavior"
|
||||||
|
description: "A clear and concise description of what you expected to happen."
|
||||||
|
placeholder: Tell us what you expect to see.
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
- type: textarea
|
||||||
|
id: "screenshots-logs"
|
||||||
|
attributes:
|
||||||
|
label: "Screenshots, videos, and logs"
|
||||||
|
description: "If applicable, add screenshots/videos/logs to help us understand your problem. While optional, these help expedite the time in which your bug is addressed."
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
- type: dropdown
|
||||||
|
id: "os"
|
||||||
|
attributes:
|
||||||
|
label: "Operating system (OS)"
|
||||||
|
multiple: false
|
||||||
|
options:
|
||||||
|
- "Select an OS"
|
||||||
|
- macOS
|
||||||
|
- Linux
|
||||||
|
- Windows
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: input
|
||||||
|
id: "os-version"
|
||||||
|
attributes:
|
||||||
|
label: "Operating system and version"
|
||||||
|
description: "For example: Debian 11.2"
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: input
|
||||||
|
id: "local-shell-version"
|
||||||
|
attributes:
|
||||||
|
label: "Shell Version"
|
||||||
|
description: "For example, `bash 4.0` e.g. Run `bash --version` or `zsh --version`"
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
- type: input
|
||||||
|
id: "warp-version"
|
||||||
|
attributes:
|
||||||
|
label: "Current Warp version"
|
||||||
|
description: "Open the Settings Dialog (CMD-,) using the Command Palette or by clicking the three dots > Settings > Account. Once you're on the Account page click the copy icon that's to the right of the version number. `v0.2047.04.07.47.47.stable_47`"
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
- type: dropdown
|
||||||
|
id: "regression"
|
||||||
|
attributes:
|
||||||
|
label: "Regression"
|
||||||
|
description: "Is this a regression (used to work in a previous Warp version)?"
|
||||||
|
multiple: false
|
||||||
|
options:
|
||||||
|
- "No, this bug or issue has existed throughout my experience using Warp"
|
||||||
|
- "Yes, this bug started recently or with an X Warp version"
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: input
|
||||||
|
id: "warp-version-regression-date"
|
||||||
|
attributes:
|
||||||
|
label: "Recent working Warp date"
|
||||||
|
description: "Most recent date that Warp worked as expected"
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
- type: textarea
|
||||||
|
id: "additional-context"
|
||||||
|
attributes:
|
||||||
|
label: "Additional context"
|
||||||
|
description: "Add any other context about the problem here. If using Warp on Linux, tell us if you're using X11 or Wayland. If the issue is graphical, run Warp with the following command `RUST_LOG=wgpu_core=info,wgpu_hal=info MESA_DEBUG=1 EGL_LOG_LEVEL=debug warp-terminal` attach logs and tell us the result of `eglinfo`."
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
- type: dropdown
|
||||||
|
id: "blocker"
|
||||||
|
attributes:
|
||||||
|
label: "Does this block you from using Warp daily?"
|
||||||
|
description: "All feedback will be reviewed, even if you select 'No'."
|
||||||
|
multiple: false
|
||||||
|
options:
|
||||||
|
- "No"
|
||||||
|
- "Yes, this issue prevents me from using Warp daily."
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: dropdown
|
||||||
|
id: "terminals"
|
||||||
|
attributes:
|
||||||
|
label: "Is this an issue only in Warp?"
|
||||||
|
description: "Verifying this issue doesn't happen in other terminals helps us to prioritize the fix"
|
||||||
|
multiple: false
|
||||||
|
options:
|
||||||
|
- "Yes, I confirmed that this only happens in Warp, not other terminals."
|
||||||
|
- "No, this issue happens in Warp and other terminals."
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: dropdown
|
||||||
|
id: "linear-label-bug"
|
||||||
|
attributes:
|
||||||
|
label: "Warp Internal (ignore): linear-label:b9d78064-c89e-4973-b153-5178a31ee54e"
|
||||||
|
multiple: false
|
||||||
|
options:
|
||||||
|
- Ignore
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
name: Feature Request
|
||||||
|
description: "Have a great new idea? Please search through our existing feature requests, and upvote it if it's already exists."
|
||||||
|
labels: ["enhancement"]
|
||||||
|
body:
|
||||||
|
- type: checkboxes
|
||||||
|
attributes:
|
||||||
|
label: "Pre-submit Checks"
|
||||||
|
options:
|
||||||
|
- label: "I have [searched Warp feature requests](https://github.com/warpdotdev/warp/issues?q=is%3Aissue+label%3AFEATURE) and there are no duplicates"
|
||||||
|
required: true
|
||||||
|
- label: "I have [searched Warp docs](https://docs.warp.dev) and my feature is not there"
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: "describe-solution"
|
||||||
|
attributes:
|
||||||
|
label: "Describe the solution you'd like?"
|
||||||
|
description: "A clear and concise description of what you want to happen."
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: "related-to-problem"
|
||||||
|
attributes:
|
||||||
|
label: "Is your feature request related to a problem? Please describe."
|
||||||
|
description: "A clear and concise description of what the problem is."
|
||||||
|
placeholder: "I'm always frustrated when [...]"
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
- type: textarea
|
||||||
|
id: "additional-context"
|
||||||
|
attributes:
|
||||||
|
label: "Additional context"
|
||||||
|
description: "Add any other context or screenshots about the feature request here. If you want to upload a picture you can drag one in from Finder."
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
- type: dropdown
|
||||||
|
id: "os"
|
||||||
|
attributes:
|
||||||
|
label: "Operating system (OS)"
|
||||||
|
multiple: false
|
||||||
|
options:
|
||||||
|
- "Select an OS"
|
||||||
|
- macOS
|
||||||
|
- Linux
|
||||||
|
- Windows
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: dropdown
|
||||||
|
id: "importance"
|
||||||
|
attributes:
|
||||||
|
label: "How important is this feature to you?"
|
||||||
|
multiple: false
|
||||||
|
options:
|
||||||
|
- "1 (Not too important)"
|
||||||
|
- "2"
|
||||||
|
- "3"
|
||||||
|
- "4"
|
||||||
|
- "5 (Can't work without it!)"
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: dropdown
|
||||||
|
id: "linear-label-feature"
|
||||||
|
attributes:
|
||||||
|
label: "Warp Internal (ignore) - linear-label:39cc6478-1249-4ee7-950b-c428edfeecd1"
|
||||||
|
multiple: false
|
||||||
|
options:
|
||||||
|
- Ignore
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
name: SSH Warpify Issues? Use this template
|
||||||
|
description: "Issue template specialized for the circumstances where SSH Warpification with tmux fails"
|
||||||
|
labels: ["bug","area:ssh"]
|
||||||
|
body:
|
||||||
|
- type: checkboxes
|
||||||
|
attributes:
|
||||||
|
label: "Pre-submit Checks"
|
||||||
|
options:
|
||||||
|
- label: "I have [searched Warp SSH issues](https://github.com/warpdotdev/Warp/issues?q=is%3Aissue+is%3Aopen+label%3ASSH-TMUX) and there are no duplicates"
|
||||||
|
required: true
|
||||||
|
- label: "I have [searched Warp known issues page](https://docs.warp.dev/help/known-issues) and my issue is not there"
|
||||||
|
required: true
|
||||||
|
- label: "I have technical issue and have included the logs (helps expedite the bug fix). [Log instructions](https://docs.warp.dev/support-and-community/troubleshooting-and-support/sending-us-feedback#gathering-warp-logs)"
|
||||||
|
required: false
|
||||||
|
- type: textarea
|
||||||
|
id: "shell-output"
|
||||||
|
attributes:
|
||||||
|
label: "Include shell output"
|
||||||
|
description: "Click on the top right icon of your terminal block, select `Copy`, and paste the contents here. Please redact any personal details."
|
||||||
|
render: shell
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: "screenshots-logs"
|
||||||
|
attributes:
|
||||||
|
label: "Screenshots, videos, and logs"
|
||||||
|
description: "If applicable, add screenshots/videos/logs to help us understand your problem. While optional, these help expedite the time in which your bug is addressed."
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
- type: dropdown
|
||||||
|
id: "os"
|
||||||
|
attributes:
|
||||||
|
label: "Operating system (OS)"
|
||||||
|
multiple: false
|
||||||
|
options:
|
||||||
|
- "Select an OS"
|
||||||
|
- macOS
|
||||||
|
- Linux
|
||||||
|
- Windows
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: input
|
||||||
|
id: "os-version"
|
||||||
|
attributes:
|
||||||
|
label: "Operating system and version"
|
||||||
|
description: "For example: Debian 11.2"
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: input
|
||||||
|
id: "warp-version"
|
||||||
|
attributes:
|
||||||
|
label: "Warp Version"
|
||||||
|
description: "Warp Version"
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
- type: dropdown
|
||||||
|
id: "linear-label-ssh-tmux"
|
||||||
|
attributes:
|
||||||
|
label: "Warp Internal (ignore) - linear-label:7a739baa-09c3-499e-a0c9-a1a16c090597"
|
||||||
|
multiple: false
|
||||||
|
options:
|
||||||
|
- Ignore
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
- type: dropdown
|
||||||
|
id: "linear-label-bug"
|
||||||
|
attributes:
|
||||||
|
label: "Warp Internal (ignore): linear-label:b8107fdf-ba31-488d-b103-d271c89cac3e"
|
||||||
|
multiple: false
|
||||||
|
options:
|
||||||
|
- Ignore
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
@@ -0,0 +1,115 @@
|
|||||||
|
name: Legacy SSH Issues? Use this template
|
||||||
|
description: "(Legacy) Issue template specialized for the circumstances where the shell doesn't bootstrap as a subshell over SSH"
|
||||||
|
labels: ["bug","area:ssh"]
|
||||||
|
body:
|
||||||
|
- type: checkboxes
|
||||||
|
attributes:
|
||||||
|
label: "Pre-submit Checks"
|
||||||
|
options:
|
||||||
|
- label: "I have [searched Warp SSH issues](https://github.com/warpdotdev/Warp/issues?q=is%3Aissue+is%3Aopen+label%3ASSH) and there are no duplicates"
|
||||||
|
required: true
|
||||||
|
- label: "I have [searched Warp known issues page](https://docs.warp.dev/help/known-issues) and my issue is not there"
|
||||||
|
required: true
|
||||||
|
- label: "I have technical issue and have included the logs (helps expedite the bug fix). [Log instructions](https://docs.warp.dev/support-and-community/troubleshooting-and-support/sending-us-feedback#gathering-warp-logs)"
|
||||||
|
required: false
|
||||||
|
- type: dropdown
|
||||||
|
id: "os"
|
||||||
|
attributes:
|
||||||
|
label: "Operating system (OS)"
|
||||||
|
multiple: false
|
||||||
|
options:
|
||||||
|
- "Select an OS"
|
||||||
|
- macOS
|
||||||
|
- Linux
|
||||||
|
- Windows
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: input
|
||||||
|
id: "os-version"
|
||||||
|
attributes:
|
||||||
|
label: "Operating System and Version"
|
||||||
|
description: "For example: Debian 11.2"
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: input
|
||||||
|
id: "local-shell-version"
|
||||||
|
attributes:
|
||||||
|
label: "Local Shell Version"
|
||||||
|
description: "Output of `echo $BASH_VERSION` or `echo $ZSH_VERSION`"
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: input
|
||||||
|
id: "remote-shell-version"
|
||||||
|
attributes:
|
||||||
|
label: "Remote Shell Version"
|
||||||
|
description: "Output of `echo $BASH_VERSION` or `echo $ZSH_VERSION`"
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: input
|
||||||
|
id: "warp-version"
|
||||||
|
attributes:
|
||||||
|
label: "Warp Version"
|
||||||
|
description: "Click the 3-dots menu docked to the right of the tabs in Warp and click the copy button, e.g. `v0.2022.05.30.09.10.stable_01`"
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: checkboxes
|
||||||
|
id: "rcfiles"
|
||||||
|
attributes:
|
||||||
|
label: "Have you tried commenting out my system & user rc files?"
|
||||||
|
description: "If the issue is with your system and user rc files, please isolate the issue by [debugging your rc files](https://docs.warp.dev/help/known-issues#configuring-and-debugging-your-rc-files)."
|
||||||
|
options:
|
||||||
|
- label: "Yes"
|
||||||
|
- type: textarea
|
||||||
|
id: "screenshots-logs"
|
||||||
|
attributes:
|
||||||
|
label: "Screenshots, videos, and logs"
|
||||||
|
description: "If applicable, add screenshots/videos/logs to help us understand your problem. While optional, these help expedite the time in which your bug is addressed."
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
- type: textarea
|
||||||
|
id: "xtrace-output"
|
||||||
|
attributes:
|
||||||
|
label: "Include shell xtrace output"
|
||||||
|
description: "1. Ensure you're on the 3.31 version of Warp or later. 2. Run WARP_DEBUG_MODE=1 ssh YOUR-HOSTNAME-HERE 3. Save the full output starting from the ssh command block. If you’re not comfortable posting the outputs to a public issue, you can email feedback@warp.dev. "
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: dropdown
|
||||||
|
id: "blocker"
|
||||||
|
attributes:
|
||||||
|
label: "Does this block you from using Warp daily?"
|
||||||
|
description: "All feedback will be reviewed, even if you select 'No'."
|
||||||
|
multiple: false
|
||||||
|
options:
|
||||||
|
- "No"
|
||||||
|
- "Yes, this issue prevents me from using Warp daily."
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: dropdown
|
||||||
|
id: "terminals"
|
||||||
|
attributes:
|
||||||
|
label: "Is this an issue only in Warp?"
|
||||||
|
description: "Verifying this issue doesn't happen in other terminals helps us to prioritize the fix"
|
||||||
|
multiple: false
|
||||||
|
options:
|
||||||
|
- "Yes, I confirmed that this only happens in Warp, not other terminals."
|
||||||
|
- "No, this issue happens in Warp and other terminals."
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: dropdown
|
||||||
|
id: "linear-label-ssh"
|
||||||
|
attributes:
|
||||||
|
label: "Warp Internal (ignore) - linear-label:e7dfaa84-5fdb-4a00-b754-d8912da923fa"
|
||||||
|
multiple: false
|
||||||
|
options:
|
||||||
|
- Ignore
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
|
- type: dropdown
|
||||||
|
id: "linear-label-bug"
|
||||||
|
attributes:
|
||||||
|
label: "Warp Internal (ignore): linear-label:b8107fdf-ba31-488d-b103-d271c89cac3e"
|
||||||
|
multiple: false
|
||||||
|
options:
|
||||||
|
- Ignore
|
||||||
|
validations:
|
||||||
|
required: false
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
blank_issues_enabled: false
|
||||||
|
contact_links:
|
||||||
|
- name: Warp GitHub Search
|
||||||
|
url: https://github.com/warpdotdev/warp/issues?q=is%3Aissue+is%3Aopen+a+sort%3Areactions-%2B1-desc
|
||||||
|
about: Search Warp's open bugs and enhancements.
|
||||||
|
- name: Warp Documentation
|
||||||
|
url: https://docs.warp.dev/support-and-billing/known-issues?q=
|
||||||
|
about: Documentation for features, known issues, troubleshooting, keyboard shortcuts, telemetry table, and more!
|
||||||
|
- name: Warp Changelog
|
||||||
|
url: https://docs.warp.dev/changelog
|
||||||
|
about: See our changelog to learn about new features, improvements, bug fixes, and Oz updates.
|
||||||
|
- name: Contact Us
|
||||||
|
url: https://docs.warp.dev/support-and-community/troubleshooting-and-support/sending-us-feedback
|
||||||
|
about: Submit feedback and contact the Warp support team for things like Billing, Enterprise, and other issues.
|
||||||
|
- name: Warp Preview Slack Community
|
||||||
|
url: https://go.warp.dev/join-preview
|
||||||
|
about: Discuss give feedback on the new features with other Warp Preview users.
|
||||||
|
- name: Warp Discord Community
|
||||||
|
url: https://discord.com/invite/warpdotdev
|
||||||
|
about: Discuss what you want to see in a Modern ADE with a community of other Warp users.
|
||||||
|
- name: Warp Homepage
|
||||||
|
url: https://www.warp.dev/
|
||||||
|
about: Homepage for all things Warp.
|
||||||
|
- name: Warp Licenses
|
||||||
|
url: https://docs.warp.dev/support-and-community/privacy-security-and-licensing/open-source-licenses
|
||||||
|
about: See the open source projects Warp depends on and their licenses.
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
## Justification
|
||||||
|
Why do we need this cherry-pick? Does it align with our [guidelines](https://www.notion.so/warpdev/How-We-Work-Releases-8e7d7cb4f2ca44b880fa1d0ae4876473?pvs=4)?
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
How did you test this change? What automated tests did you add? If you didn't add any new tests, what's your justification for not adding any?
|
||||||
|
|
||||||
|
## Validation
|
||||||
|
- [ ] I've cut a new WarpDev to validate this change
|
||||||
|
- [ ] If this change is Wednesday or later, there's a dedicated bug bash scheduled for either this change or the release
|
||||||
|
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
This PR template helps ensure that as we launch new features we appropriately communicate, track, document, make them accessible, etc.
|
||||||
|
|
||||||
|
## PRD checklist
|
||||||
|
|
||||||
|
- [ ] Plan for how to measure success quantified by metrics
|
||||||
|
|
||||||
|
## Coding checklist
|
||||||
|
|
||||||
|
- [ ] Test in dev for a week
|
||||||
|
- [ ] Telemetry in code
|
||||||
|
- [ ] A11y (if applicable, see [testing a11y guide](https://docs.google.com/document/d/1-H0bWss5Qw18ZpIYg-RUvN7_db1MVdWfOb5UF_GLxNc/edit?usp=sharing) for more info)
|
||||||
|
- [ ] Add to Command Palette (if applicable)
|
||||||
|
- [ ] Add toggle setting(s) to command palette (if applicable)
|
||||||
|
- [ ] Add to Mac Menu (if applicable)
|
||||||
|
- [ ] Add keybinding (if applicable), see [actions audit for inspiration](https://docs.google.com/spreadsheets/d/1C56ZIqDGjJi873-HAPdnT2DofC3Z6G-aJMYeQeERHx4/edit#gid=0)
|
||||||
|
- [ ] Sanity check within the app that it does not clash other keybindings
|
||||||
|
- [ ] No sensitive info in logs
|
||||||
|
- [ ] No crashes on dev related to the feature
|
||||||
|
- [ ] No performance regression on dev. See [dashboard](https://warp.metabaseapp.com/dashboard/1519-dev-performance-by-version?shell=zsh)
|
||||||
|
- [ ] Feature works fine, and no regression, over SSH. See [instructions](https://github.com/warpdotdev/warp-internal/tree/master/app/tests/ssh/README.md) on how to get a VM.
|
||||||
|
- [ ] Have we explicitly brainstormed how this feature will be discovered by developers?
|
||||||
|
- [ ] Link to Figma mocks
|
||||||
|
- [ ] Tested on multiple themes (both dark and light)
|
||||||
|
- [ ] If the feature being released relies on some server API, has that server API been stable on production for at least one full server release cycle? See [here](https://www.notion.so/warpdev/How-to-add-a-new-full-stack-feature-8412cede405a4ec194b32bdd4b951035?pvs=4#73b202f939834b97ab1fbdf7fc82cd53) for more details.
|
||||||
|
|
||||||
|
|
||||||
|
## Content checklist
|
||||||
|
|
||||||
|
- [ ] Help content
|
||||||
|
- [ ] Changelog entry (add entry below)
|
||||||
|
- [ ] [Telemetry entry](https://docs.warp.dev/getting-started/privacy#exhaustive-telemetry-table) (if applicable)
|
||||||
|
- [ ] Metrics dashboard in Metabase
|
||||||
|
- [ ] Tweet (if appropriate)
|
||||||
|
- [ ] Blog post (if appropriate)
|
||||||
|
|
||||||
|
## Changelog
|
||||||
|
|
||||||
|
CHANGELOG-NEW-FEATURE: {{Insert a changelog entry here}}
|
||||||
@@ -0,0 +1,182 @@
|
|||||||
|
# STAKEHOLDERS
|
||||||
|
#
|
||||||
|
# Maps source-code paths to subject-matter experts for issue and PR triage.
|
||||||
|
# Format follows CODEOWNERS: <pattern> @owner1 @owner2 ...
|
||||||
|
#
|
||||||
|
# Used by the triage-new-issues workflow to assign SMEs.
|
||||||
|
# Source of truth: warpdotdev/feedback-triage-bot ownership-areas.md
|
||||||
|
|
||||||
|
# Default fallback: FA leads are tagged when no more specific path matches
|
||||||
|
/ @vorporeal @alokedesai @zachbai @bnavetta @szgupta @jefflloyd
|
||||||
|
|
||||||
|
###########################################################################
|
||||||
|
# Team: App
|
||||||
|
###########################################################################
|
||||||
|
|
||||||
|
# Conversation and session restoration / cloud-synced conversations / planning / Warp Drive
|
||||||
|
/app/src/ai/agent_conversations_model.rs @seemeroland
|
||||||
|
/app/src/ai/persisted_workspace.rs @seemeroland
|
||||||
|
/app/src/ai/restored_conversations.rs @seemeroland
|
||||||
|
/app/src/ai/conversation_details_panel.rs @seemeroland
|
||||||
|
/app/src/drive/ @seemeroland
|
||||||
|
/app/src/cloud_object/ @seemeroland
|
||||||
|
/app/src/workflows/ @seemeroland
|
||||||
|
/app/src/server/cloud_objects/ @seemeroland
|
||||||
|
/app/src/terminal/find/ @seemeroland
|
||||||
|
/app/src/uri/ @seemeroland
|
||||||
|
|
||||||
|
# Code editor / LSP / notebooks / codebase context
|
||||||
|
/app/src/code/ @kevinyang372
|
||||||
|
/app/src/editor/ @kevinyang372
|
||||||
|
/app/src/notebooks/ @kevinyang372 @bnavetta
|
||||||
|
/app/src/ai/get_relevant_files/ @kevinyang372
|
||||||
|
/crates/ai/src/index/ @kevinyang372
|
||||||
|
/crates/ai/src/project_context/ @kevinyang372
|
||||||
|
/crates/editor/ @kevinyang372 @bnavetta
|
||||||
|
/crates/lsp/ @kevinyang372
|
||||||
|
/crates/repo_metadata/ @kevinyang372
|
||||||
|
|
||||||
|
# Settings and keybindings
|
||||||
|
/app/src/settings/ @lucieleblanc
|
||||||
|
/app/src/settings_view/ @lucieleblanc
|
||||||
|
/app/src/resource_center/keybindings_page.rs @lucieleblanc
|
||||||
|
|
||||||
|
# Input editor / window, tab, and pane management / grep tool call
|
||||||
|
/app/src/terminal/input/ @vkodithala
|
||||||
|
/app/src/pane_group/ @vkodithala
|
||||||
|
/app/src/tab.rs @vkodithala
|
||||||
|
/crates/warp_ripgrep/ @vkodithala @moirahuang @szgupta
|
||||||
|
|
||||||
|
# Command palette
|
||||||
|
/app/src/command_palette.rs @acarl005
|
||||||
|
/app/src/palette.rs @acarl005
|
||||||
|
/app/src/search/command_palette/ @acarl005
|
||||||
|
|
||||||
|
# @ context, slash commands, and global search / file tree
|
||||||
|
/app/src/context_chips/ @moirahuang
|
||||||
|
/app/src/search/ai_context_menu/ @moirahuang
|
||||||
|
/app/src/search/files/ @moirahuang
|
||||||
|
/app/src/search/search_results_menu/ @moirahuang
|
||||||
|
/app/src/search/slash_command_menu/ @moirahuang
|
||||||
|
/app/src/terminal/input/slash_commands/ @moirahuang
|
||||||
|
/app/src/code/file_tree/ @moirahuang
|
||||||
|
/app/src/workspace/view/global_search/ @moirahuang
|
||||||
|
|
||||||
|
# Onboarding / code review and git diff
|
||||||
|
/app/src/ai/onboarding.rs @kevinchevalier
|
||||||
|
/app/src/code_review/ @kevinchevalier
|
||||||
|
/app/src/code/diff_viewer.rs @kevinchevalier
|
||||||
|
/app/src/code/inline_diff.rs @kevinchevalier
|
||||||
|
/crates/onboarding/ @kevinchevalier
|
||||||
|
|
||||||
|
# MCP and skills
|
||||||
|
/app/src/ai/mcp/ @peicodes
|
||||||
|
/app/src/ai/skills/ @peicodes
|
||||||
|
/crates/mcp/ @peicodes
|
||||||
|
|
||||||
|
# Conversation management / credit usage footer / input UI / natural-language detection
|
||||||
|
/app/src/ai/active_agent_views_model.rs @harryalbert
|
||||||
|
/app/src/ai/conversation_navigation/ @harryalbert
|
||||||
|
/app/src/ai/conversation_status_ui.rs @harryalbert
|
||||||
|
/app/src/ai/request_usage_model.rs @harryalbert
|
||||||
|
/app/src/search/command_palette/conversations/ @harryalbert
|
||||||
|
/app/src/workspace/view/conversation_list/ @harryalbert
|
||||||
|
/app/src/terminal/input/conversations/ @harryalbert
|
||||||
|
/app/src/input_classifier.rs @harryalbert
|
||||||
|
/crates/input_classifier/ @harryalbert
|
||||||
|
/crates/natural_language_detection/ @harryalbert
|
||||||
|
|
||||||
|
# Blocklist UX / modality and cloud mode UI / shell compatibility / completions and bootstrap / warpifying
|
||||||
|
/app/src/ai/blocklist/ @zachbai
|
||||||
|
/app/src/root_view.rs @zachbai
|
||||||
|
/app/src/terminal/bootstrap.rs @zachbai
|
||||||
|
/app/src/terminal/warpify/ @zachbai
|
||||||
|
/app/src/settings_view/warpify_page.rs @zachbai
|
||||||
|
/app/assets/bundled/bootstrap/ @zachbai
|
||||||
|
/crates/warp_completer/ @zachbai @szgupta @alokedesai
|
||||||
|
|
||||||
|
# Agent mode
|
||||||
|
/app/src/ai/agent/ @zachbai
|
||||||
|
|
||||||
|
# Image attachment, voice input, and passive suggestions
|
||||||
|
/app/src/ai/attachment_utils.rs @Advait-M
|
||||||
|
/app/src/ai/voice/ @Advait-M
|
||||||
|
/app/src/ai/predict/ @Advait-M
|
||||||
|
/app/src/ai/blocklist/passive_suggestions/ @Advait-M
|
||||||
|
/app/src/voice/ @Advait-M
|
||||||
|
/crates/voice_input/ @Advait-M
|
||||||
|
|
||||||
|
# UI framework
|
||||||
|
/crates/warpui/ @vorporeal
|
||||||
|
/crates/warpui_core/ @vorporeal
|
||||||
|
/crates/warpui_extras/ @vorporeal
|
||||||
|
/crates/ui_components/ @vorporeal
|
||||||
|
|
||||||
|
# Conversation rewind / CLI agent UI / macOS/Linux platform issues / performance issues
|
||||||
|
/app/src/terminal/cli_agent.rs @zachbai
|
||||||
|
/app/src/terminal/cli_agent_sessions/ @zachbai
|
||||||
|
/app/src/terminal/input/rewind/ @alokedesai
|
||||||
|
/app/src/workspace/rewind_confirmation_dialog.rs @alokedesai
|
||||||
|
/app/src/platform/mac/ @alokedesai
|
||||||
|
/resources/linux/ @acarl005
|
||||||
|
|
||||||
|
# /pr-comments
|
||||||
|
/app/src/search/slash_command_menu/static_commands/commands.rs @lucieleblanc
|
||||||
|
|
||||||
|
# Grep tool call UI
|
||||||
|
/app/src/ai/blocklist/action_model/execute/grep.rs @vkodithala
|
||||||
|
|
||||||
|
###########################################################################
|
||||||
|
# Team: Platform
|
||||||
|
###########################################################################
|
||||||
|
|
||||||
|
# REST API and SDKs
|
||||||
|
/app/src/server/server_api/ @ianhodge
|
||||||
|
/crates/graphql/ @ianhodge
|
||||||
|
/crates/http_client/ @ianhodge
|
||||||
|
/crates/http_server/ @ianhodge
|
||||||
|
/crates/warp_server_client/ @ianhodge
|
||||||
|
|
||||||
|
# Session sharing
|
||||||
|
/app/src/terminal/shared_session/ @abhishekp106 @szgupta @bnavetta
|
||||||
|
|
||||||
|
# Agent profiles and execution permissions
|
||||||
|
/app/src/ai/cloud_agent_config/ @abhishekp106
|
||||||
|
/app/src/ai/cloud_agent_settings.rs @abhishekp106
|
||||||
|
/app/src/ai/execution_profiles/ @abhishekp106
|
||||||
|
/app/src/terminal/profile_model_selector.rs @abhishekp106
|
||||||
|
/app/src/settings_view/execution_profile_view.rs @abhishekp106
|
||||||
|
|
||||||
|
# Environment setup / agent management view
|
||||||
|
/app/src/ai/agent_management/ @liliwilson
|
||||||
|
/app/src/ai/cloud_environments/ @liliwilson
|
||||||
|
/app/src/settings_view/agent_assisted_environment_modal.rs @liliwilson
|
||||||
|
/app/src/settings_view/environments_page/ @liliwilson
|
||||||
|
/app/src/settings_view/environments_page.rs @liliwilson
|
||||||
|
/app/src/settings_view/update_environment_form.rs @liliwilson
|
||||||
|
|
||||||
|
# OAuth, Slack, Linear, and GitHub integrations
|
||||||
|
/app/src/auth/ @Legoben @jefflloyd @vorporeal @bnavetta
|
||||||
|
/app/src/linear.rs @Legoben @jefflloyd @vorporeal @bnavetta
|
||||||
|
/app/src/server/server_api/integrations.rs @Legoben @jefflloyd @vorporeal @bnavetta
|
||||||
|
/app/src/ai/agent_sdk/oauth_flow.rs @Legoben @jefflloyd @vorporeal @bnavetta
|
||||||
|
/app/src/ai/mcp/manager/oauth.rs @Legoben @jefflloyd @vorporeal @bnavetta
|
||||||
|
/app/src/ai/mcp/templatable_manager/oauth.rs @Legoben @jefflloyd @vorporeal @bnavetta
|
||||||
|
|
||||||
|
# Self-hosted agents
|
||||||
|
/crates/remote_server/ @kevinyang372 @alokedesai
|
||||||
|
|
||||||
|
# Scheduled agents / Warp CLI / cloud and ambient agents
|
||||||
|
/app/src/ai/ambient_agents/ @bnavetta
|
||||||
|
/app/src/terminal/view/ambient_agent/ @bnavetta
|
||||||
|
/app/src/workspace/view/launch_modal/oz_launch.rs @bnavetta
|
||||||
|
/crates/warp_cli/ @bnavetta @ianhodge
|
||||||
|
|
||||||
|
# Agent SDK and platform crates
|
||||||
|
/app/src/ai/agent_sdk/ @bnavetta @ianhodge
|
||||||
|
/crates/isolation_platform/ @bnavetta @ianhodge
|
||||||
|
/crates/managed_secrets/ @bnavetta @ianhodge
|
||||||
|
/crates/warp_web_event_bus/ @bnavetta @ianhodge
|
||||||
|
|
||||||
|
# Shell completions in the Warp CLI
|
||||||
|
/crates/warp_cli/src/completions.rs @zachbai @szgupta @alokedesai
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
FROM archlinux:base-devel
|
||||||
|
|
||||||
|
ARG USERNAME=build
|
||||||
|
# We use 1001 as the user UID here because 1000 is the UID for `runneradmin`,
|
||||||
|
# but the actual jobs are run as `runner` (with UID 1001).
|
||||||
|
ARG USER_UID=1001
|
||||||
|
ARG USER_GID=$USER_UID
|
||||||
|
|
||||||
|
# Set the packager name in makepkg.conf (so it doesn't say "Unknown Packager")
|
||||||
|
# when someone queries package info for our packages.
|
||||||
|
RUN echo 'PACKAGER="Warp Linux Maintainers <linux-maintainers@warp.dev>"' >> /etc/makepkg.conf
|
||||||
|
|
||||||
|
# Fetch package lists and install sudo, git, and protobuf (git is needed by
|
||||||
|
# cargo to resolve git dependencies during license generation, and protobuf
|
||||||
|
# provides protoc for generating rust bindings of protobuf APIs).
|
||||||
|
RUN pacman -Sy --needed --noconfirm sudo git protobuf
|
||||||
|
|
||||||
|
# Downgrade fakeroot to version 1.34. Version 1.35 switches from calling
|
||||||
|
# `close` in a loop to using the new `close_range` syscall, which seems to
|
||||||
|
# be unavailable in the Ubuntu 20.04 environment that hosts this Docker
|
||||||
|
# container.
|
||||||
|
#
|
||||||
|
# Syscall discussion: https://archlinuxarm.org/forum/viewtopic.php?f=15&t=16943
|
||||||
|
# Downgrade discussion: https://github.com/docker/for-mac/issues/7331
|
||||||
|
RUN pacman -U --noconfirm https://archive.archlinux.org/packages/f/fakeroot/fakeroot-1.34-1-x86_64.pkg.tar.zst
|
||||||
|
|
||||||
|
# Install cargo-about, which we require for bundling third-party licenses.
|
||||||
|
RUN pacman -S --needed --noconfirm cargo-about
|
||||||
|
|
||||||
|
# Create our build user and give them sudo privileges.
|
||||||
|
RUN groupadd --gid $USER_GID $USERNAME \
|
||||||
|
&& useradd --uid $USER_UID --gid $USER_GID -m $USERNAME \
|
||||||
|
&& echo $USERNAME ALL=\(root\) NOPASSWD:ALL > /etc/sudoers.d/$USERNAME \
|
||||||
|
&& chmod 0440 /etc/sudoers.d/$USERNAME
|
||||||
|
|
||||||
|
# Run as our build user instead of root, as makepkg must be run as a non-root
|
||||||
|
# user.
|
||||||
|
USER $USERNAME
|
||||||
|
|
||||||
|
COPY entrypoint.sh /entrypoint.sh
|
||||||
|
|
||||||
|
ENTRYPOINT ["/entrypoint.sh"]
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
# Important note: This must be run after we build the release binary; this only
|
||||||
|
# packages up an already-built binary.
|
||||||
|
|
||||||
|
name: Bundle Arch Linux package
|
||||||
|
description: Bundles an Arch Linux package for the given release channel.
|
||||||
|
inputs:
|
||||||
|
channel:
|
||||||
|
description: The channel for which we want to build an Arch package.
|
||||||
|
requred: true
|
||||||
|
release-tag:
|
||||||
|
description: The git release tag.
|
||||||
|
required: true
|
||||||
|
arch:
|
||||||
|
description: The architecture we are building for
|
||||||
|
required: true
|
||||||
|
artifact:
|
||||||
|
description: The artifact type to build (app or cli)
|
||||||
|
required: false
|
||||||
|
default: app
|
||||||
|
|
||||||
|
runs:
|
||||||
|
using: composite
|
||||||
|
steps:
|
||||||
|
- name: Build Docker image
|
||||||
|
shell: bash
|
||||||
|
run: docker build -t arch-bundle-builder ${{ github.action_path }}
|
||||||
|
|
||||||
|
- name: Run in container
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
docker run --rm \
|
||||||
|
-v "${{ github.workspace }}:/github/workspace" \
|
||||||
|
-v "${{ github.workspace }}/target:/github/workspace/target" \
|
||||||
|
-w /github/workspace \
|
||||||
|
-v "${CARGO_HOME:-$HOME/.cargo}/git:/home/build/.cargo/git" \
|
||||||
|
-v "${CARGO_HOME:-$HOME/.cargo}/registry:/home/build/.cargo/registry" \
|
||||||
|
-e GIT_RELEASE_TAG="$GIT_RELEASE_TAG" \
|
||||||
|
-e GITHUB_ACTIONS="$GITHUB_ACTIONS" \
|
||||||
|
-e GITHUB_OUTPUT="/dev/null" \
|
||||||
|
arch-bundle-builder \
|
||||||
|
${{ inputs.channel }} ${{ inputs.release-tag }} ${{ inputs.arch }} ${{ inputs.artifact }}
|
||||||
+13
@@ -0,0 +1,13 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
BUILD_ARCH="$3"
|
||||||
|
BUILD_ARCH="${BUILD_ARCH:-$(uname -m)}"
|
||||||
|
|
||||||
|
# Ensure we build with the most up-to-date package list. This could get stale due to Docker
|
||||||
|
# filesystem caching.
|
||||||
|
sudo pacman -Sy
|
||||||
|
|
||||||
|
# Run the bundle script, specifying the release channel and tag, skipping
|
||||||
|
# building the binary (as we have already done so), and only bundling the
|
||||||
|
# Arch package.
|
||||||
|
./script/bundle --channel $1 --release-tag $2 --skip-build --packages arch --arch $BUILD_ARCH --artifact $4
|
||||||
@@ -0,0 +1,170 @@
|
|||||||
|
name: "Docubot"
|
||||||
|
description: "Helps Warp engineers identify PRs that need documentation updates in GitBook"
|
||||||
|
inputs:
|
||||||
|
prompt:
|
||||||
|
description: "Prompt to send to Warp"
|
||||||
|
required: true
|
||||||
|
warp_api_key:
|
||||||
|
description: "Warp API key"
|
||||||
|
required: true
|
||||||
|
warp_channel:
|
||||||
|
description: "Warp release channel (e.g., dev, preview, stable)"
|
||||||
|
required: false
|
||||||
|
default: "dev"
|
||||||
|
profile_id:
|
||||||
|
description: "Warp profile ID"
|
||||||
|
required: false
|
||||||
|
github_token:
|
||||||
|
description: "GitHub token for cloning private repositories"
|
||||||
|
required: false
|
||||||
|
runs:
|
||||||
|
using: composite
|
||||||
|
steps:
|
||||||
|
- name: Setup Warp CLI
|
||||||
|
uses: ./.github/actions/setup_warp_cli
|
||||||
|
with:
|
||||||
|
warp_channel: ${{ inputs.warp_channel }}
|
||||||
|
- name: Clone Warp GitBook repository
|
||||||
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||||
|
with:
|
||||||
|
repository: warpdotdev/gitbook
|
||||||
|
token: ${{ inputs.github_token }}
|
||||||
|
path: gitbook
|
||||||
|
- name: Create prompt file
|
||||||
|
shell: bash
|
||||||
|
# TODO: use sdk (python or typescript) for this
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
envsubst < .github/actions/docubot/prompt.txt > prompt.output.txt
|
||||||
|
env:
|
||||||
|
# GitHub Repository Context
|
||||||
|
GITHUB_REPOSITORY: ${{ github.repository }}
|
||||||
|
GITHUB_REPOSITORY_OWNER: ${{ github.repository_owner }}
|
||||||
|
GITHUB_REPOSITORY_OWNER_ID: ${{ github.repository_owner_id }}
|
||||||
|
GITHUB_REPOSITORY_ID: ${{ github.repository_id }}
|
||||||
|
GITHUB_REPOSITORY_URL: ${{ github.repositoryUrl }}
|
||||||
|
|
||||||
|
# GitHub Event Context
|
||||||
|
GITHUB_EVENT_NAME: ${{ github.event_name }}
|
||||||
|
GITHUB_EVENT_PATH: ${{ github.event_path }}
|
||||||
|
GITHUB_EVENT_ACTION: ${{ github.event.action }}
|
||||||
|
|
||||||
|
# GitHub Workflow Context
|
||||||
|
GITHUB_WORKFLOW: ${{ github.workflow }}
|
||||||
|
GITHUB_WORKFLOW_REF: ${{ github.workflow_ref }}
|
||||||
|
GITHUB_WORKFLOW_SHA: ${{ github.workflow_sha }}
|
||||||
|
GITHUB_JOB: ${{ github.job }}
|
||||||
|
GITHUB_RUN_ID: ${{ github.run_id }}
|
||||||
|
GITHUB_RUN_NUMBER: ${{ github.run_number }}
|
||||||
|
GITHUB_RUN_ATTEMPT: ${{ github.run_attempt }}
|
||||||
|
|
||||||
|
# GitHub Actor Context
|
||||||
|
GITHUB_ACTOR: ${{ github.actor }}
|
||||||
|
GITHUB_ACTOR_ID: ${{ github.actor_id }}
|
||||||
|
GITHUB_TRIGGERING_ACTOR: ${{ github.triggering_actor }}
|
||||||
|
|
||||||
|
# GitHub Git Context
|
||||||
|
GITHUB_REF: ${{ github.ref }}
|
||||||
|
GITHUB_REF_NAME: ${{ github.ref_name }}
|
||||||
|
GITHUB_REF_PROTECTED: ${{ github.ref_protected }}
|
||||||
|
GITHUB_REF_TYPE: ${{ github.ref_type }}
|
||||||
|
GITHUB_SHA: ${{ github.sha }}
|
||||||
|
GITHUB_HEAD_REF: ${{ github.head_ref }}
|
||||||
|
GITHUB_BASE_REF: ${{ github.base_ref }}
|
||||||
|
|
||||||
|
# Issue/PR Context
|
||||||
|
PR_OR_ISSUE_TITLE: ${{ github.event.issue.title || github.event.pull_request.title }}
|
||||||
|
PR_OR_ISSUE_BODY: ${{ github.event.issue.body || github.event.pull_request.body }}
|
||||||
|
PR_OR_ISSUE_NUMBER: ${{ github.event.issue.number || github.event.pull_request.number }}
|
||||||
|
PR_OR_ISSUE_URL: ${{ github.event.issue.html_url || github.event.pull_request.html_url }}
|
||||||
|
PR_OR_ISSUE_STATE: ${{ github.event.issue.state || github.event.pull_request.state }}
|
||||||
|
PR_OR_ISSUE_CREATED_AT: ${{ github.event.issue.created_at || github.event.pull_request.created_at }}
|
||||||
|
PR_OR_ISSUE_UPDATED_AT: ${{ github.event.issue.updated_at || github.event.pull_request.updated_at }}
|
||||||
|
PR_OR_ISSUE_USER_LOGIN: ${{ github.event.issue.user.login || github.event.pull_request.user.login }}
|
||||||
|
PR_OR_ISSUE_USER_ID: ${{ github.event.issue.user.id || github.event.pull_request.user.id }}
|
||||||
|
PR_OR_ISSUE_ASSIGNEES: ${{ toJson(github.event.issue.assignees) || toJson(github.event.pull_request.assignees) }}
|
||||||
|
PR_OR_ISSUE_LABELS: ${{ toJson(github.event.issue.labels) || toJson(github.event.pull_request.labels) }}
|
||||||
|
|
||||||
|
# Pull Request Specific Context
|
||||||
|
PR_HEAD_SHA: ${{ github.event.pull_request.head.sha }}
|
||||||
|
PR_HEAD_REF: ${{ github.event.pull_request.head.ref }}
|
||||||
|
PR_HEAD_REPO_FULL_NAME: ${{ github.event.pull_request.head.repo.full_name }}
|
||||||
|
PR_BASE_SHA: ${{ github.event.pull_request.base.sha }}
|
||||||
|
PR_BASE_REF: ${{ github.event.pull_request.base.ref }}
|
||||||
|
PR_BASE_REPO_FULL_NAME: ${{ github.event.pull_request.base.repo.full_name }}
|
||||||
|
PR_DRAFT: ${{ github.event.pull_request.draft }}
|
||||||
|
PR_MERGED: ${{ github.event.pull_request.merged }}
|
||||||
|
PR_MERGEABLE: ${{ github.event.pull_request.mergeable }}
|
||||||
|
PR_MERGEABLE_STATE: ${{ github.event.pull_request.mergeable_state }}
|
||||||
|
PR_COMMITS: ${{ github.event.pull_request.commits }}
|
||||||
|
PR_ADDITIONS: ${{ github.event.pull_request.additions }}
|
||||||
|
PR_DELETIONS: ${{ github.event.pull_request.deletions }}
|
||||||
|
PR_CHANGED_FILES: ${{ github.event.pull_request.changed_files }}
|
||||||
|
|
||||||
|
# Issue Specific Context
|
||||||
|
ISSUE_CLOSED_AT: ${{ github.event.issue.closed_at }}
|
||||||
|
ISSUE_MILESTONE: ${{ toJson(github.event.issue.milestone) }}
|
||||||
|
|
||||||
|
# Comment Context (for issue_comment events)
|
||||||
|
COMMENT_BODY: ${{ github.event.comment.body }}
|
||||||
|
COMMENT_USER_LOGIN: ${{ github.event.comment.user.login }}
|
||||||
|
COMMENT_USER_ID: ${{ github.event.comment.user.id }}
|
||||||
|
COMMENT_CREATED_AT: ${{ github.event.comment.created_at }}
|
||||||
|
COMMENT_UPDATED_AT: ${{ github.event.comment.updated_at }}
|
||||||
|
COMMENT_HTML_URL: ${{ github.event.comment.html_url }}
|
||||||
|
|
||||||
|
# Documentation Repository Context
|
||||||
|
GITBOOK_PATH: ${{ format('{0}/gitbook', github.workspace) }}
|
||||||
|
|
||||||
|
- name: Configure git identity
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
git config user.name "Warp Agent"
|
||||||
|
git config user.email "agent@warp.dev"
|
||||||
|
|
||||||
|
- name: Run Warp agent
|
||||||
|
shell: bash
|
||||||
|
env:
|
||||||
|
WARP_API_KEY: ${{ inputs.warp_api_key }}
|
||||||
|
GH_TOKEN: ${{ inputs.github_token }}
|
||||||
|
PROFILE_ID: ${{ inputs.profile_id }}
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
# Determine the CLI command name based on channel suffix
|
||||||
|
case "${{ inputs.warp_channel }}" in
|
||||||
|
stable|stable_release|"") suffix="" ;;
|
||||||
|
preview|preview_release) suffix="-preview" ;;
|
||||||
|
dev|dev_release) suffix="-dev" ;;
|
||||||
|
local) suffix="-local" ;;
|
||||||
|
integration|integration_test) suffix="-integration" ;;
|
||||||
|
*) suffix="" ;;
|
||||||
|
esac
|
||||||
|
cmd="warp-cli${suffix}"
|
||||||
|
|
||||||
|
# Basic check the binary exists
|
||||||
|
if ! command -v "$cmd" >/dev/null 2>&1; then
|
||||||
|
echo "Error: $cmd not found on PATH after installation"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Execute the agent run
|
||||||
|
"$cmd" agent run \
|
||||||
|
--prompt "$(cat prompt.output.txt)" \
|
||||||
|
--api-key "$WARP_API_KEY" \
|
||||||
|
--cwd "$GITHUB_WORKSPACE" \
|
||||||
|
${PROFILE_ID:+--profile "$PROFILE_ID"}
|
||||||
|
- name: Upload Warp logs
|
||||||
|
if: ${{ always() }}
|
||||||
|
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||||
|
with:
|
||||||
|
name: warp-logs.log
|
||||||
|
path: /home/runner/.local/state/warp-terminal-dev/warp_dev.log
|
||||||
|
if-no-files-found: ignore
|
||||||
|
- name: Upload Warp prompt
|
||||||
|
if: ${{ always() }}
|
||||||
|
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||||
|
with:
|
||||||
|
name: prompt.output.txt
|
||||||
|
path: prompt.output.txt
|
||||||
|
if-no-files-found: ignore
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
You are Warp, the world's best AI coding assistant for helping with GitHub PRs and issues. Here is the context for your current task:
|
||||||
|
|
||||||
|
<pr_or_issue_title>
|
||||||
|
$PR_OR_ISSUE_TITLE
|
||||||
|
</pr_or_issue_title>
|
||||||
|
|
||||||
|
<pr_or_issue_body>
|
||||||
|
$PR_OR_ISSUE_BODY
|
||||||
|
</pr_or_issue_body>
|
||||||
|
|
||||||
|
<pr_or_issue_url>
|
||||||
|
$PR_OR_ISSUE_URL
|
||||||
|
</pr_or_issue_url>
|
||||||
|
|
||||||
|
<changed_files>
|
||||||
|
$PR_CHANGED_FILES
|
||||||
|
</changed_files>
|
||||||
|
|
||||||
|
<trigger_comment>
|
||||||
|
$COMMENT_USER_LOGIN: $COMMENT_BODY
|
||||||
|
</trigger_comment>
|
||||||
|
|
||||||
|
Your persona: You are **docubot**, living inside Warp's internal codebase. When triggered, you read the context of the invoking PR and update Warp's documentation repo (`https://github.com/warpdotdev/gitbook`). The GitBook repository is already available locally at `$GITBOOK_PATH` - you have direct access to it and should use it without attempting to clone it again. The repo is organized into Markdown files and assets. Your job is to create a new branch, apply accurate documentation changes, and open a well-scoped PR with a high-quality description. You need to post the link to the PR to the original PR in Warp-internal where the changes were made.
|
||||||
|
|
||||||
|
**Available Context Variables:**
|
||||||
|
- `$PR_OR_ISSUE_NUMBER`: The PR/issue number in warp-internal
|
||||||
|
- `$PR_OR_ISSUE_USER_LOGIN`: The GitHub username of the original PR author
|
||||||
|
- `$PR_OR_ISSUE_URL`: The URL of the original warp-internal PR
|
||||||
|
- `$PR_OR_ISSUE_TITLE`: The title of the original PR
|
||||||
|
- `$GH_TOKEN`: The token you should use to authenticate with GitHub when creating the PR. DO NOT reveal the contents of this secret token.
|
||||||
|
|
||||||
|
Key rules:
|
||||||
|
- **Truth first**: Only document behavior present in the PR or linked issues. Add `<!-- TODO -->` if something is unclear.
|
||||||
|
- **Scope**: Update only affected pages plus any directly impacted references. Don’t rewrite unrelated sections.
|
||||||
|
- **Consistency**: Match existing style, tone, and structure in GitBook.
|
||||||
|
- **Assets**: Place images in `.gitbook/assets/`, use kebab-case names, and add alt text.
|
||||||
|
- **Privacy**: No secrets, tokens, or internal data in examples or screenshots.
|
||||||
|
- **Cross-linking**: Update or add links between related sections where useful.
|
||||||
|
|
||||||
|
Commit & PR conventions:
|
||||||
|
- Branch name: `docubot/<short-slug-from-invoking-pr-title>`
|
||||||
|
- Commit message style: `docs(area): concise summary of change`
|
||||||
|
- PR title: `Docs: <concise summary> (from <repo>#<PR>)`
|
||||||
|
- PR description must include:
|
||||||
|
- **Summary** of what changed
|
||||||
|
- **Pages/sections updated**
|
||||||
|
- **Why** the change was made
|
||||||
|
|
||||||
|
IMPORTANT OUTPUT INSTRUCTIONS:
|
||||||
|
- You are only allowed to leave **one comment**. Do so at the end of your run. Use the `gh` CLI tool to add a comment. You can (and should) use Markdown to format this.
|
||||||
|
- If code/doc changes are requested, make the changes locally, then update them in the remote branch via `git add`, `git commit`, and `git push -u` before posting your final comment. You MUST push the local branch with `-u`, otherwise the `gh` CLI will fail when you try to create the PR.
|
||||||
|
- Your comment should either:
|
||||||
|
1. Provide a clear PR summary + next steps (if you opened a docs PR), or
|
||||||
|
2. Explain blockers/clarifications needed.
|
||||||
|
|
||||||
|
ADDITIONAL WORKFLOW REQUIREMENTS:
|
||||||
|
- **After successfully creating a GitBook PR**: You MUST comment on the original warp-internal PR to:
|
||||||
|
1. Provide a link to the new GitBook documentation PR
|
||||||
|
2. Tag the original PR author (@$PR_OR_ISSUE_USER_LOGIN) as a reviewer to review the documentation changes
|
||||||
|
3. Include a brief summary of what documentation was updated
|
||||||
|
- **Steps for commenting on original PR**:
|
||||||
|
1. First, ensure you have the GitBook PR URL from your newly created PR
|
||||||
|
2. Use `gh pr comment $PR_OR_ISSUE_NUMBER --body "[comment content]"` in the warp-internal repository
|
||||||
|
3. The comment should follow this format:
|
||||||
|
```
|
||||||
|
**Documentation Updated**
|
||||||
|
|
||||||
|
I've created a documentation PR based on the changes in this PR:
|
||||||
|
**GitBook PR**: [Insert actual GitBook PR URL here]
|
||||||
|
|
||||||
|
**What was updated:**
|
||||||
|
- [Brief bullet points of documentation changes made]
|
||||||
|
|
||||||
|
@$PR_OR_ISSUE_USER_LOGIN - Please review the documentation changes to ensure they accurately reflect your implementation.
|
||||||
|
```
|
||||||
|
- **Important**: Replace `[Insert actual GitBook PR URL here]` with the real URL of the GitBook PR you created
|
||||||
|
- **Important**: Replace `[Brief bullet points of documentation changes made]` with actual details of what you updated
|
||||||
|
- **Repository context**: Ensure you're in the warp-internal repository when running the `gh pr comment` command, not in the GitBook repository
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
name: Get Channel Config
|
||||||
|
description: Provides a variety of config data for the given channel type.
|
||||||
|
inputs:
|
||||||
|
config_file:
|
||||||
|
description: The path to the configuration file.
|
||||||
|
required: true
|
||||||
|
channel:
|
||||||
|
description: The channel to retrieve configuration data for.
|
||||||
|
required: true
|
||||||
|
outputs:
|
||||||
|
channel:
|
||||||
|
description: The channel that we're building a release for. (This is the same as the input value.)
|
||||||
|
value: ${{ steps.get-config.outputs.channel }}
|
||||||
|
type:
|
||||||
|
description: The release type, either "nightly" or "weekly".
|
||||||
|
value: ${{ steps.get-config.outputs.type }}
|
||||||
|
is_prerelease:
|
||||||
|
description: Whether this channel is a prerelease channel.
|
||||||
|
value: ${{ steps.get-config.outputs.is_prerelease }}
|
||||||
|
is_autopush:
|
||||||
|
description: Whether new builds for this channel automatically trigger channel versions updates.
|
||||||
|
value: ${{ steps.get-config.outputs.is_autopush }}
|
||||||
|
release_base_name:
|
||||||
|
description: The base name for the GitHub release, to which the release number is appended.
|
||||||
|
value: ${{ steps.get-config.outputs.release_base_name }}
|
||||||
|
release_body_text:
|
||||||
|
description: The body text for the GitHub release.
|
||||||
|
value: ${{ steps.get-config.outputs.release_body_text }}
|
||||||
|
sentry_project:
|
||||||
|
description: The Sentry project under which a new release should be created.
|
||||||
|
value: ${{ steps.get-config.outputs.sentry_project }}
|
||||||
|
sentry_environment:
|
||||||
|
description: The environment to set for this channel's Sentry release.
|
||||||
|
value: ${{ steps.get-config.outputs.sentry_environment }}
|
||||||
|
changelog_slack_channel:
|
||||||
|
description: The Slack channel where the release's changelog should be posted.
|
||||||
|
value: ${{ steps.get-config.outputs.changelog_slack_channel }}
|
||||||
|
gcs_cache_control_value:
|
||||||
|
description: The Cache-Control value to set for release artifacts in Cloud Storage.
|
||||||
|
value: ${{ steps.get-config.outputs.gcs_cache_control_value }}
|
||||||
|
web_gcs_bucket_prefix:
|
||||||
|
description: The GCS bucket prefix for web artifacts. (Determines if they go in a staging or prod bucket.)
|
||||||
|
value: ${{ steps.get-config.outputs.web_gcs_bucket_prefix }}
|
||||||
|
|
||||||
|
runs:
|
||||||
|
using: composite
|
||||||
|
steps:
|
||||||
|
- id: get-config
|
||||||
|
env:
|
||||||
|
CONFIG_FILE: ${{ inputs.config_file }}
|
||||||
|
CHANNEL: ${{ inputs.channel }}
|
||||||
|
run: |
|
||||||
|
echo ::echo::on
|
||||||
|
jq -e '.channels[] | select(.channel == $ENV.CHANNEL)' $CONFIG_FILE > /dev/null
|
||||||
|
# Fail if the given channel is not found in the JSON release configuration.
|
||||||
|
[ $? -eq 0 ] || (echo ::error::"Channel $CHANNEL not found in release configuration" && exit 1)
|
||||||
|
jq -rc '.channels[] | select(.channel == $ENV.CHANNEL) | to_entries | map("\(.key)=\(.value)")[]' $CONFIG_FILE >> $GITHUB_OUTPUT
|
||||||
|
echo ::echo::off
|
||||||
|
shell: bash
|
||||||
@@ -0,0 +1,139 @@
|
|||||||
|
name: Prepare Environment
|
||||||
|
description: A shared set of setup steps to prepare the runtime environment.
|
||||||
|
inputs:
|
||||||
|
target_os:
|
||||||
|
required: true
|
||||||
|
description: The target OS that we're building for ("macos", "linux", "windows", or "wasm").
|
||||||
|
is_self_hosted:
|
||||||
|
required: true
|
||||||
|
description: Whether or not the workflow is running on a self-hosted runner.
|
||||||
|
ref:
|
||||||
|
description: If set, the ref to checkout.
|
||||||
|
install_test_deps:
|
||||||
|
description: If true, dependencies needed for running tests will be installed.
|
||||||
|
install_release_deps:
|
||||||
|
description: If true, dependencies needed for building release artifacts will be installed (e.g., cargo-about for license generation).
|
||||||
|
cache_key:
|
||||||
|
description: If set, the cache key to use. If unset, the cache key will be derived from the target OS.
|
||||||
|
|
||||||
|
runs:
|
||||||
|
using: composite
|
||||||
|
steps:
|
||||||
|
- name: Make GitHub token available in the environment
|
||||||
|
shell: bash
|
||||||
|
run: echo "GH_TOKEN=${{ github.token }}" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Set up git for Windows
|
||||||
|
if: ${{ inputs.target_os == 'windows' }}
|
||||||
|
shell: bash
|
||||||
|
run: git config --system core.longpaths true
|
||||||
|
|
||||||
|
- name: Set CARGO_HOME location
|
||||||
|
if: ${{ inputs.target_os == 'windows' }}
|
||||||
|
shell: bash
|
||||||
|
run: echo "CARGO_HOME=C:\cargo" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Determine repository root
|
||||||
|
id: repo-root
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
# The action lives at <repo-root>/.github/actions/prepare_environment.
|
||||||
|
# Derive the repository root from the action path.
|
||||||
|
REPO_ROOT=$(cd "${{ github.action_path }}/../../.." && pwd)
|
||||||
|
echo "path=$REPO_ROOT" >> $GITHUB_OUTPUT
|
||||||
|
# Normalize $GITHUB_WORKSPACE through cd/pwd so that path formats
|
||||||
|
# match on Windows (where $GITHUB_WORKSPACE uses backslashes but
|
||||||
|
# pwd produces MSYS-style forward-slash paths).
|
||||||
|
WORKSPACE=$(cd "$GITHUB_WORKSPACE" && pwd)
|
||||||
|
if [ "$REPO_ROOT" = "$WORKSPACE" ]; then
|
||||||
|
echo "is-workspace-root=true" >> $GITHUB_OUTPUT
|
||||||
|
else
|
||||||
|
echo "is-workspace-root=false" >> $GITHUB_OUTPUT
|
||||||
|
fi
|
||||||
|
|
||||||
|
- uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
|
||||||
|
if: ${{ inputs.is_self_hosted != 'true' && !startsWith(runner.name, 'nsc-runner') }}
|
||||||
|
with:
|
||||||
|
# Make sure x86_64-unknown-linux-gnu and wasm32-unknown-unknown builds
|
||||||
|
# don't share a cache, despite running on the same _host_ architecture.
|
||||||
|
key: ${{ inputs.cache_key != '' && inputs.cache_key || inputs.target_os }}
|
||||||
|
# Only cache runs on the master branch (caches on feature branches
|
||||||
|
# are not reusable).
|
||||||
|
save-if: ${{ github.ref == 'refs/heads/master' }}
|
||||||
|
|
||||||
|
- name: Set up Namespace cache
|
||||||
|
if: ${{ startsWith(runner.name, 'nsc-runner') }}
|
||||||
|
uses: namespacelabs/nscloud-cache-action@a90bb5d4b27522ce881c6e98eebd7d7e6d1653f9 # v1.4.2
|
||||||
|
with:
|
||||||
|
cache: |
|
||||||
|
rust
|
||||||
|
${{ inputs.target_os == 'macos' && 'brew' || '' }}
|
||||||
|
|
||||||
|
- name: Install cargo-binstall
|
||||||
|
uses: cargo-bins/cargo-binstall@dc19f1e48450eefe5a29b8da6c6b00a87d730b37 # v1.18.1
|
||||||
|
env:
|
||||||
|
BINSTALL_VERSION: 1.14.4
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
cd "${{ steps.repo-root.outputs.path }}"
|
||||||
|
if ${{ inputs.target_os == 'macos' }}; then
|
||||||
|
# Try to install warp-channel-config but don't complain if it cannot
|
||||||
|
# be installed.
|
||||||
|
./script/install_channel_config || true
|
||||||
|
if ${{ inputs.install_release_deps == 'true' }}; then
|
||||||
|
./script/install_cargo_release_deps
|
||||||
|
else
|
||||||
|
./script/install_cargo_build_deps
|
||||||
|
fi
|
||||||
|
elif ${{ inputs.target_os == 'linux' }}; then
|
||||||
|
# Update the apt cache before installing dependencies - it can be slow, so the install scripts
|
||||||
|
# don't do it.
|
||||||
|
sudo apt-get update -y
|
||||||
|
|
||||||
|
if ${{ inputs.install_test_deps == 'true' }}; then
|
||||||
|
./script/linux/install_test_deps
|
||||||
|
elif ${{ inputs.install_release_deps == 'true' }}; then
|
||||||
|
./script/linux/install_build_deps
|
||||||
|
./script/install_cargo_release_deps
|
||||||
|
else
|
||||||
|
./script/linux/install_build_deps
|
||||||
|
fi
|
||||||
|
elif ${{ inputs.target_os == 'windows' }}; then
|
||||||
|
./script/windows/install_build_deps.ps1
|
||||||
|
if ${{ inputs.install_release_deps == 'true' }}; then
|
||||||
|
./script/install_cargo_release_deps
|
||||||
|
fi
|
||||||
|
elif ${{ inputs.target_os == 'wasm' }}; then
|
||||||
|
# Update the apt cache before installing dependencies - it can be slow, so the install scripts
|
||||||
|
# don't do it (to avoid doing it more than once).
|
||||||
|
sudo apt update -y
|
||||||
|
./script/wasm/install_build_deps
|
||||||
|
else
|
||||||
|
echo "::error::Missing logic to install build dependencies for OS \"${{ inputs.target_os }}\""
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Install Node
|
||||||
|
uses: actions/setup-node@3235b876344d2a9aa001b8d1453c930bba69e610 # v3.9.1
|
||||||
|
with:
|
||||||
|
node-version: 20.9.0
|
||||||
|
|
||||||
|
- name: Enable Corepack
|
||||||
|
# Enables NodeJS's Corepack tool, which is necessary to use the right version of `yarn` to
|
||||||
|
# build TS command signatures.
|
||||||
|
shell: bash
|
||||||
|
run: corepack enable
|
||||||
|
|
||||||
|
- name: Install protoc for generating rust bindings of protobuf APIs
|
||||||
|
# Install protoc for generating rust bindings of protobuf APIs.
|
||||||
|
uses: ConorMacBride/install-package@3e7ad059e07782ee54fa35f827df52aae0626f30 # v1
|
||||||
|
with:
|
||||||
|
brew: protobuf
|
||||||
|
choco: protoc
|
||||||
|
|
||||||
|
- uses: maxim-lobanov/setup-xcode@ed7a3b1fda3918c0306d1b724322adc0b8cc0a90 # v1.7.0
|
||||||
|
if: ${{ inputs.target_os == 'macos' && inputs.is_self_hosted != 'true' }}
|
||||||
|
with:
|
||||||
|
xcode-version: '26'
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
# Please see the documentation for all configuration options:
|
||||||
|
# https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
|
||||||
|
|
||||||
|
version: 2
|
||||||
|
updates:
|
||||||
|
- package-ecosystem: "cargo"
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: "daily"
|
||||||
|
reviewers:
|
||||||
|
- "warpdotdev/tech-leads"
|
||||||
|
registries:
|
||||||
|
- github-private
|
||||||
|
# Only send security updates, not general version updates.
|
||||||
|
open-pull-requests-limit: 0
|
||||||
|
- package-ecosystem: "github-actions"
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: "daily"
|
||||||
|
reviewers:
|
||||||
|
- "warpdotdev/tech-leads"
|
||||||
|
cooldown:
|
||||||
|
# Don't update to any action release that is less than two weeks old.
|
||||||
|
default-days: 14
|
||||||
|
groups:
|
||||||
|
# Group all non-major updates of official actions together - they're lower-risk.
|
||||||
|
official-actions:
|
||||||
|
applies-to: version-updates
|
||||||
|
patterns:
|
||||||
|
- "actions/*"
|
||||||
|
update-types:
|
||||||
|
- "minor"
|
||||||
|
- "patch"
|
||||||
|
# Group all non-major updates of Namespace actions together - they're lower-risk.
|
||||||
|
namespace-actions:
|
||||||
|
applies-to: version-updates
|
||||||
|
patterns:
|
||||||
|
- "namespacelabs/*"
|
||||||
|
- "namespace-actions/*"
|
||||||
|
update-types:
|
||||||
|
- "minor"
|
||||||
|
- "patch"
|
||||||
|
registries:
|
||||||
|
# Allow dependabot to access our private GitHub repositories.
|
||||||
|
github-private:
|
||||||
|
type: git
|
||||||
|
url: https://github.com
|
||||||
|
username: x-access-token
|
||||||
|
# This is a PAT (personal access token) for the warpmachineuser
|
||||||
|
# GitHub user.
|
||||||
|
password: ${{secrets.DEPENDABOT_PRIVATE_REPO_ACCESS_TOKEN}}
|
||||||
@@ -0,0 +1,168 @@
|
|||||||
|
{
|
||||||
|
"labels": {
|
||||||
|
"bug": {
|
||||||
|
"color": "d73a4a",
|
||||||
|
"description": "Something isn't working."
|
||||||
|
},
|
||||||
|
"enhancement": {
|
||||||
|
"color": "a2eeef",
|
||||||
|
"description": "New feature or request."
|
||||||
|
},
|
||||||
|
"documentation": {
|
||||||
|
"color": "0075ca",
|
||||||
|
"description": "Improvements or additions to documentation."
|
||||||
|
},
|
||||||
|
"duplicate": {
|
||||||
|
"color": "cfd3d7",
|
||||||
|
"description": "This issue or pull request already exists."
|
||||||
|
},
|
||||||
|
"needs-info": {
|
||||||
|
"color": "fbca04",
|
||||||
|
"description": "More issue-specific reporter detail is needed before the problem can be confidently triaged."
|
||||||
|
},
|
||||||
|
"triaged": {
|
||||||
|
"color": "5319e7",
|
||||||
|
"description": "Issue has received an initial automated triage pass."
|
||||||
|
},
|
||||||
|
"ready-to-spec": {
|
||||||
|
"color": "1d76db",
|
||||||
|
"description": "The issue is ready for a product and technical spec."
|
||||||
|
},
|
||||||
|
"needs-mocks": {
|
||||||
|
"color": "f48c06",
|
||||||
|
"description": "The issue requires UI mocks from from Warp's design team before implementation can begin."
|
||||||
|
},
|
||||||
|
"ready-to-implement": {
|
||||||
|
"color": "0e8a16",
|
||||||
|
"description": "The issue is ready for implementation work."
|
||||||
|
},
|
||||||
|
"repro:high": {
|
||||||
|
"color": "b60205",
|
||||||
|
"description": "The report includes enough evidence that the issue appears highly reproducible."
|
||||||
|
},
|
||||||
|
"repro:medium": {
|
||||||
|
"color": "fbca04",
|
||||||
|
"description": "The report suggests a plausible repro path, but some uncertainty remains."
|
||||||
|
},
|
||||||
|
"repro:low": {
|
||||||
|
"color": "d4c5f9",
|
||||||
|
"description": "The report points to a real problem, but reproduction details are weak or inconsistent."
|
||||||
|
},
|
||||||
|
"repro:unknown": {
|
||||||
|
"color": "6e7681",
|
||||||
|
"description": "The report does not provide enough evidence to estimate reproducibility yet."
|
||||||
|
},
|
||||||
|
"accessibility": {
|
||||||
|
"color": "7306a2",
|
||||||
|
"description": "Accessibility issues or requests."
|
||||||
|
},
|
||||||
|
"os:mac": {
|
||||||
|
"color": "f5f5f7",
|
||||||
|
"description": "macOS-specific behavior, regressions, or requests."
|
||||||
|
},
|
||||||
|
"os:linux": {
|
||||||
|
"color": "e95420",
|
||||||
|
"description": "Linux-specific behavior, regressions, or requests."
|
||||||
|
},
|
||||||
|
"os:windows": {
|
||||||
|
"color": "0078d6",
|
||||||
|
"description": "Windows-specific behavior, regressions, or requests."
|
||||||
|
},
|
||||||
|
"area:agent": {
|
||||||
|
"color": "0052cc",
|
||||||
|
"description": "Agent workflows, conversations, prompts, cloud mode, and AI-specific UI."
|
||||||
|
},
|
||||||
|
"area:auth": {
|
||||||
|
"color": "5319e7",
|
||||||
|
"description": "Authentication, login, SSO, session management, and account security."
|
||||||
|
},
|
||||||
|
"area:billing": {
|
||||||
|
"color": "8b5cf6",
|
||||||
|
"description": "Pricing, plans, subscriptions, payment, and billing management."
|
||||||
|
},
|
||||||
|
"area:code-review": {
|
||||||
|
"color": "0e8a16",
|
||||||
|
"description": "Git diff views, review UI, review comments, and PR-focused agent flows."
|
||||||
|
},
|
||||||
|
"area:completions": {
|
||||||
|
"color": "2da44e",
|
||||||
|
"description": "Standard shell completions, argument completions, and path completion."
|
||||||
|
},
|
||||||
|
"area:editor-notebooks": {
|
||||||
|
"color": "1d76db",
|
||||||
|
"description": "Editors, notebooks, markdown rendering, LSP, and code display."
|
||||||
|
},
|
||||||
|
"area:launch-configs": {
|
||||||
|
"color": "c2e0c6",
|
||||||
|
"description": "Launch configurations, workflows, tab configs, and automation entry points."
|
||||||
|
},
|
||||||
|
"area:mcp": {
|
||||||
|
"color": "bfdadc",
|
||||||
|
"description": "MCP server integrations, tool connections, and resource providers."
|
||||||
|
},
|
||||||
|
"area:onboarding": {
|
||||||
|
"color": "f9d0c4",
|
||||||
|
"description": "First-run experience, onboarding flows, and related callouts."
|
||||||
|
},
|
||||||
|
"area:performance": {
|
||||||
|
"color": "e36209",
|
||||||
|
"description": "General application performance, responsiveness, and resource usage."
|
||||||
|
},
|
||||||
|
"area:performance:cpu": {
|
||||||
|
"color": "d93f0b",
|
||||||
|
"description": "CPU utilization, process efficiency, and compute-bound performance."
|
||||||
|
},
|
||||||
|
"area:performance:memory": {
|
||||||
|
"color": "d93f0b",
|
||||||
|
"description": "Memory usage, allocation, leaks, and memory-bound performance."
|
||||||
|
},
|
||||||
|
"area:performance:gpu": {
|
||||||
|
"color": "d93f0b",
|
||||||
|
"description": "GPU utilization, rendering performance, and graphics resource usage."
|
||||||
|
},
|
||||||
|
"area:search": {
|
||||||
|
"color": "0366d6",
|
||||||
|
"description": "Global search, command palette, and content discovery."
|
||||||
|
},
|
||||||
|
"area:settings-keybindings": {
|
||||||
|
"color": "d876e3",
|
||||||
|
"description": "Settings UI, preferences, keybindings, and keyboard-shortcut management."
|
||||||
|
},
|
||||||
|
"area:shell-terminal": {
|
||||||
|
"color": "006b75",
|
||||||
|
"description": "Terminal output, shell integration, prompt rendering, and block display."
|
||||||
|
},
|
||||||
|
"area:skills": {
|
||||||
|
"color": "c6e2d6",
|
||||||
|
"description": "Agent skills, skill authoring, and skill execution."
|
||||||
|
},
|
||||||
|
"area:suggestions": {
|
||||||
|
"color": "7dc4e4",
|
||||||
|
"description": "AI-predictive suggestions, prompt predictions, next-command suggestions, and auto-suggest heuristics."
|
||||||
|
},
|
||||||
|
"area:terminal-input": {
|
||||||
|
"color": "0a6b5e",
|
||||||
|
"description": "Terminal command-line input, cursor movement, key handling, and input editing."
|
||||||
|
},
|
||||||
|
"area:ssh": {
|
||||||
|
"color": "00008b",
|
||||||
|
"description": "SSH and remote-session behavior, including tmux-related terminal flows."
|
||||||
|
},
|
||||||
|
"area:ui-framework": {
|
||||||
|
"color": "5319e7",
|
||||||
|
"description": "Core Warp UI framework, rendering, layout, and windowing infrastructure."
|
||||||
|
},
|
||||||
|
"area:warp-drive": {
|
||||||
|
"color": "0e8a16",
|
||||||
|
"description": "Warp Drive objects, sync, sharing, cloud object management, and persisted artifacts."
|
||||||
|
},
|
||||||
|
"area:window-tabs-panes": {
|
||||||
|
"color": "fef2c0",
|
||||||
|
"description": "Window, tab, pane, and layout management."
|
||||||
|
},
|
||||||
|
"area:workspace": {
|
||||||
|
"color": "0550ae",
|
||||||
|
"description": "File tree, workspace navigation, project switching, and working-directory management."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
## Description
|
||||||
|
<!-- Please remember to add your design buddy onto the PR for review, if it contains any UI changes! -->
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
<!--
|
||||||
|
How did you test this change? What automated tests did you add? If you didn't add any new tests, what's your justification for not adding any?
|
||||||
|
|
||||||
|
If you're not sure whether you should add a test, check our testing policy: https://www.notion.so/warpdev/How-We-Code-at-Warp-257fe43d556e4b3c8dfd42f70004cc72#1f97825450504baa9c5fd87a737daa09
|
||||||
|
-->
|
||||||
|
|
||||||
|
## Server API dependencies
|
||||||
|
<!-- You may remove this section if your PR does not have any server dependencies. -->
|
||||||
|
- [ ] Is this change necessary to make the client compatible with a desired [server API breaking change](https://www.notion.so/warpdev/How-to-safely-introduce-server-API-breaking-changes-0aa805ff5d5d41fd8834f3c95caba0b4?pvs=4#d55ecf8aea3449949d3c33b0e67f6800)?
|
||||||
|
- [ ] Does this change rely on a [new server API](https://www.notion.so/warpdev/How-to-add-a-new-full-stack-feature-8412cede405a4ec194b32bdd4b951035?pvs=4#04da1e6a493542d68b3e998c7d339640)?
|
||||||
|
- [ ] If so, is the use of this API restricted to client channels that rely on the staging server (e.g. WarpDev)?
|
||||||
|
- [ ] Is this change enabling the use of a server API on client channels that rely on the production server (e.g. WarpStable)?
|
||||||
|
- [ ] If so, has the new server API been stable on production for at least one server release cycle? See [here](https://www.notion.so/warpdev/How-to-add-a-new-full-stack-feature-8412cede405a4ec194b32bdd4b951035?pvs=4#73b202f939834b97ab1fbdf7fc82cd53) for more details.
|
||||||
|
|
||||||
|
## Agent Mode
|
||||||
|
- [ ] Warp Agent Mode - This PR was created via Warp's AI Agent Mode
|
||||||
|
|
||||||
|
## Changelog Entries for Stable
|
||||||
|
<!--
|
||||||
|
The entries below will be used when constructing a soft-copy of the stable release changelog. Leave blank or remove the lines if no entry in the stable changelog is needed. Entries should be on the same line, without the `{{` `}}` brackets. You can use multiple lines, even of the same type. The valid suffixes are:
|
||||||
|
|
||||||
|
* NEW-FEATURE: for new, relatively sizable features. Features listed here will likely have docs / social media posts / marketing launches associated with them, so use sparingly.
|
||||||
|
* IMPROVEMENT: for new functionality of existing features.
|
||||||
|
* BUG-FIX: for fixes related to known bugs or regressions.
|
||||||
|
* IMAGE: the image specified by the URL (hosted on GCP) will be added to Dev & Preview releases. For Stable releases, see the pinned doc in the #release Slack channel.
|
||||||
|
* OZ: Oz-related updates. Use `CHANGELOG-OZ`. At most 4 Oz updates are shown in-app per release.
|
||||||
|
-->
|
||||||
|
|
||||||
|
CHANGELOG-NEW-FEATURE: {{text goes here...}}
|
||||||
|
CHANGELOG-IMPROVEMENT: {{text goes here...}}
|
||||||
|
CHANGELOG-BUG-FIX: {{text goes here...}}
|
||||||
|
CHANGELOG-BUG-FIX: {{more text goes here...}}
|
||||||
|
CHANGELOG-IMAGE: {{GCP-hosted URL goes here...}}
|
||||||
|
CHANGELOG-OZ: {{text goes here...}}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
# Release Configurations
|
||||||
|
|
||||||
|
This README file documents the format of the `release_configurations.json` file located in this directory. The file defines Warp's various release channels, and provides values for the various variables that are necessary to run the `create_new_releases.yml` GitHub workflow.
|
||||||
|
|
||||||
|
At some point, we may want to replace this document with a JSON schema file (which could be used to validate the correctness of the configuration as part of PR presubmit).
|
||||||
|
|
||||||
|
## Fields
|
||||||
|
|
||||||
|
* **channel**: The channel's unique identifier
|
||||||
|
* **type**: The release cadence. At present, the valid values are "nightly" or "weekly".
|
||||||
|
* **is_prerelease**: If true, the GitHub release for this channel will be marked as prerelease.
|
||||||
|
* **is_autopush**: If true, this channel uses the "latest" keyword in `channel_versions.json` to automatically deploy new release candidates. Non-autopush channels require a manual change in order to deploy them.
|
||||||
|
* **release_base_name**: The base name of GitHub releases created for this channel.
|
||||||
|
* **release_body_text**: The body text for GitHub releases created for this channel.
|
||||||
|
* **sentry_project**: Which Sentry project should receive crash and error reports for this channel.
|
||||||
|
* **sentry_environment**: The Sentry environment that corresponds to this channel.
|
||||||
|
* **changelog_slack_channel**: The Slack channel where new changelogs will be posted whenever a new release candidates is cut.
|
||||||
|
* **gcs_cache_control_value**: The value of the cache-control response header for release DMGs.
|
||||||
|
- **IMPORTANT!!**: the value of the cache-control header _must_ be all lowercase; uppercase values will not be respected by Cloud CDN.
|
||||||
@@ -0,0 +1,162 @@
|
|||||||
|
name: Check Approvals
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
types: [opened, synchronize, ready_for_review]
|
||||||
|
pull_request_review:
|
||||||
|
types: [submitted]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
check_owners:
|
||||||
|
name: Check OWNERS approval
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
# Skip draft PRs.
|
||||||
|
if: github.event.pull_request.draft == false
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
# We don't actually need the contents of the files, just their names.
|
||||||
|
filter: blob:none
|
||||||
|
|
||||||
|
- name: Get changed files
|
||||||
|
id: changed_files
|
||||||
|
run: |
|
||||||
|
HEAD_SHA=${{ github.event.pull_request.head.sha }}
|
||||||
|
BASE_SHA=${{ github.event.pull_request.base.sha }}
|
||||||
|
MERGE_BASE=$(git merge-base "$BASE_SHA" "$HEAD_SHA")
|
||||||
|
CHANGED_FILES=$(git diff --name-only "$MERGE_BASE" "$HEAD_SHA" | tr '\n' ' ')
|
||||||
|
echo "files=$CHANGED_FILES" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
|
- name: Get PR approvers
|
||||||
|
id: approvers
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ github.token }}
|
||||||
|
run: |
|
||||||
|
PR_NUMBER=${{ github.event.pull_request.number }}
|
||||||
|
PR_AUTHOR=${{ github.event.pull_request.user.login }}
|
||||||
|
|
||||||
|
# Get all approved reviews.
|
||||||
|
REVIEWERS=$(gh pr view "$PR_NUMBER" --json reviews --jq '[.reviews[] | select(.state == "APPROVED") | .author.login] | unique | join(" ")')
|
||||||
|
|
||||||
|
# Combine PR author (self-approves) with reviewers.
|
||||||
|
ALL_APPROVERS="$PR_AUTHOR $REVIEWERS"
|
||||||
|
echo "approvers=$ALL_APPROVERS" >> "$GITHUB_OUTPUT"
|
||||||
|
echo "has_reviewers=$( [[ -n "$REVIEWERS" ]] && echo true || echo false )" >> "$GITHUB_OUTPUT"
|
||||||
|
echo "PR author: $PR_AUTHOR"
|
||||||
|
echo "Reviewers who approved: $REVIEWERS"
|
||||||
|
echo "All approvers: $ALL_APPROVERS"
|
||||||
|
|
||||||
|
- name: Check OWNERS approval
|
||||||
|
env:
|
||||||
|
CHANGED_FILES: ${{ steps.changed_files.outputs.files }}
|
||||||
|
APPROVERS: ${{ steps.approvers.outputs.approvers }}
|
||||||
|
run: |
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Convert approvers to an array.
|
||||||
|
read -ra APPROVER_ARRAY <<< "$APPROVERS"
|
||||||
|
|
||||||
|
# Function to get owners for a file by walking up the directory tree.
|
||||||
|
get_owners_for_file() {
|
||||||
|
local file="$1"
|
||||||
|
local dir
|
||||||
|
dir=$(dirname "$file")
|
||||||
|
local owners=()
|
||||||
|
|
||||||
|
# Walk up the directory tree.
|
||||||
|
while [[ "$dir" != "." && "$dir" != "/" ]]; do
|
||||||
|
if [[ -f "$dir/OWNERS" ]]; then
|
||||||
|
# Read owners file, skip comments and empty lines.
|
||||||
|
while IFS= read -r line || [[ -n "$line" ]]; do
|
||||||
|
# Skip comments and empty lines.
|
||||||
|
line=$(echo "$line" | sed 's/#.*//' | xargs)
|
||||||
|
if [[ -n "$line" ]]; then
|
||||||
|
owners+=("$line")
|
||||||
|
fi
|
||||||
|
done < "$dir/OWNERS"
|
||||||
|
fi
|
||||||
|
dir=$(dirname "$dir")
|
||||||
|
done
|
||||||
|
|
||||||
|
# Check root directory.
|
||||||
|
if [[ -f "OWNERS" ]]; then
|
||||||
|
while IFS= read -r line || [[ -n "$line" ]]; do
|
||||||
|
line=$(echo "$line" | sed 's/#.*//' | xargs)
|
||||||
|
if [[ -n "$line" ]]; then
|
||||||
|
owners+=("$line")
|
||||||
|
fi
|
||||||
|
done < "OWNERS"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Return unique owners.
|
||||||
|
printf '%s\n' "${owners[@]}" | sort -u | tr '\n' ' ' | sed 's/ $//'
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to check if any approver is in the owners list.
|
||||||
|
has_owner_approval() {
|
||||||
|
local owners="$1"
|
||||||
|
for approver in "${APPROVER_ARRAY[@]}"; do
|
||||||
|
if echo "$owners" | grep -qw "$approver"; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
MISSING_APPROVAL=()
|
||||||
|
FILES_CHECKED=0
|
||||||
|
|
||||||
|
for file in $CHANGED_FILES; do
|
||||||
|
# Skip if file doesn't exist (deleted files).
|
||||||
|
FILES_CHECKED=$((FILES_CHECKED + 1))
|
||||||
|
|
||||||
|
OWNERS=$(get_owners_for_file "$file")
|
||||||
|
|
||||||
|
if [[ -z "$OWNERS" ]]; then
|
||||||
|
# No OWNERS files in ancestry - automatically passes.
|
||||||
|
echo "✓ $file (no OWNERS requirement)"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
if has_owner_approval "$OWNERS"; then
|
||||||
|
echo "✓ $file"
|
||||||
|
else
|
||||||
|
echo "✗ $file (owners: $OWNERS)"
|
||||||
|
MISSING_APPROVAL+=("$file")
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Files checked: $FILES_CHECKED"
|
||||||
|
echo "Approvers: ${APPROVER_ARRAY[*]}"
|
||||||
|
|
||||||
|
# Save missing files for comment step (must happen before exit).
|
||||||
|
echo "MISSING_FILES<<EOF" >> "$GITHUB_ENV"
|
||||||
|
for file in "${MISSING_APPROVAL[@]}"; do
|
||||||
|
OWNERS=$(get_owners_for_file "$file")
|
||||||
|
echo "- \`$file\` (needs approval from: $OWNERS)" >> "$GITHUB_ENV"
|
||||||
|
done
|
||||||
|
echo "EOF" >> "$GITHUB_ENV"
|
||||||
|
|
||||||
|
if [[ ${#MISSING_APPROVAL[@]} -gt 0 ]]; then
|
||||||
|
echo ""
|
||||||
|
echo "::error::The following files are missing OWNERS approval:"
|
||||||
|
for file in "${MISSING_APPROVAL[@]}"; do
|
||||||
|
OWNERS=$(get_owners_for_file "$file")
|
||||||
|
echo "::error:: - $file (needs approval from: $OWNERS)"
|
||||||
|
done
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "All files have required OWNERS approval."
|
||||||
|
|
||||||
|
- name: Comment on PR about missing approvals
|
||||||
|
if: failure() && github.event_name == 'pull_request_review' && steps.approvers.outputs.has_reviewers == 'true'
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ github.token }}
|
||||||
|
run: |
|
||||||
|
PR_NUMBER=${{ github.event.pull_request.number }}
|
||||||
|
gh pr comment "$PR_NUMBER" --body "The following files still need approval from their OWNERS:\n\n$MISSING_FILES"
|
||||||
@@ -0,0 +1,720 @@
|
|||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- 'master'
|
||||||
|
- '*_release/*'
|
||||||
|
types:
|
||||||
|
- opened
|
||||||
|
- reopened
|
||||||
|
- synchronize
|
||||||
|
- ready_for_review
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
runner_type:
|
||||||
|
required: true
|
||||||
|
type: choice
|
||||||
|
options:
|
||||||
|
- self_hosted
|
||||||
|
- github_hosted
|
||||||
|
workflow_call:
|
||||||
|
|
||||||
|
name: Warp CI
|
||||||
|
|
||||||
|
env:
|
||||||
|
CARGO_TERM_COLOR: always
|
||||||
|
NEXTEST_PROFILE: ci
|
||||||
|
WORKSPACE_TEST_ARGS: --workspace --locked --exclude command-signatures-v2
|
||||||
|
# Include only as much debug info as is necessary to see backtraces
|
||||||
|
# in test failures. This should speed up compilation somewhat and
|
||||||
|
# reduces the size of built artifacts (which addresses issues with
|
||||||
|
# running out of space on GitHub-hosted runners).
|
||||||
|
RUSTFLAGS: -C debuginfo=line-tables-only --cfg=web_sys_unstable_apis
|
||||||
|
# Ensure we open up real windows that render their contents via the GPU,
|
||||||
|
# so that we can exercise the actual rendering logic.
|
||||||
|
WARPUI_USE_REAL_DISPLAY_IN_INTEGRATION_TESTS: 1
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
# Cancel any outstanding CI workflow runs against the same PR.
|
||||||
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
params:
|
||||||
|
name: Compute workflow parameters
|
||||||
|
timeout-minutes: 3
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
# Don't automatically run CI for draft PRs, to reduce GitHub Actions costs.
|
||||||
|
#
|
||||||
|
# Also, don't run CI for repo-sync PRs _unless_ there is a merge conflict - for those,
|
||||||
|
# we'll want to make sure that the conflict resolution doesn't introduce any issues.
|
||||||
|
if: >-
|
||||||
|
github.event.pull_request.draft == false &&
|
||||||
|
(!startsWith(github.event.pull_request.head.ref, 'repo-sync/') || contains(github.event.pull_request.labels.*.name, 'repo-sync:conflict'))
|
||||||
|
outputs:
|
||||||
|
affects-database-schema: ${{ github.ref == 'master' || steps.filter.outputs.affects-database-schema }}
|
||||||
|
affects-rust-sources: ${{ github.ref == 'master' || steps.filter.outputs.affects-rust-sources }}
|
||||||
|
macos-runner: ${{ steps.mac_runner_type.outputs.value }}
|
||||||
|
wasm-runner: ${{ steps.wasm_runner_type.outputs.value }}
|
||||||
|
steps:
|
||||||
|
- name: Checkout sources
|
||||||
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
|
|
||||||
|
- name: Check changed files
|
||||||
|
uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1
|
||||||
|
id: filter
|
||||||
|
with:
|
||||||
|
filters: |
|
||||||
|
affects-database-schema:
|
||||||
|
- 'crates/persistence/src/schema.rs'
|
||||||
|
- 'crates/persistence/migrations/**'
|
||||||
|
affects-rust-sources:
|
||||||
|
- '**.rs'
|
||||||
|
|
||||||
|
- name: Determine macOS runner type
|
||||||
|
id: mac_runner_type
|
||||||
|
run: |
|
||||||
|
RUNNER_TYPE='["namespace-profile-mac-ci"]'
|
||||||
|
echo "value=$RUNNER_TYPE" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Determine wasm runner type
|
||||||
|
id: wasm_runner_type
|
||||||
|
run: |
|
||||||
|
# Use a larger (16 core, 64GB ram) runner on Linux to speed up execution time.
|
||||||
|
# https://github.com/warpdotdev/warp-internal/settings/actions/runners
|
||||||
|
RUNNER_TYPE='["ubuntu-latest-large"]'
|
||||||
|
echo "value=$RUNNER_TYPE" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
tests:
|
||||||
|
name: Run ${{ matrix.name }} tests
|
||||||
|
timeout-minutes: 25
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- os: "macos"
|
||||||
|
name: "MacOS"
|
||||||
|
runner: ${{ fromJSON(needs.params.outputs.macos-runner) }}
|
||||||
|
is_self_hosted: ${{ contains(fromJSON(needs.params.outputs.macos-runner), 'self-hosted') }}
|
||||||
|
extra_test_args: ""
|
||||||
|
|
||||||
|
- os: "linux"
|
||||||
|
name: "Linux"
|
||||||
|
# Use a larger (16 core, 64GB ram) runner on Linux to speed up execution time.
|
||||||
|
# https://github.com/warpdotdev/warp-internal/settings/actions/runners
|
||||||
|
runner: ubuntu-latest-large
|
||||||
|
# We don't (yet) have any self-hosted Linux runners.
|
||||||
|
is_self_hosted: false
|
||||||
|
extra_test_args: ""
|
||||||
|
|
||||||
|
- os: "windows"
|
||||||
|
name: "Windows"
|
||||||
|
runner: windows-latest-large
|
||||||
|
# We don't (yet) have any self-hosted Windows runners.
|
||||||
|
is_self_hosted: false
|
||||||
|
extra_test_args: "--exclude command-signatures-v2 --exclude warp_js"
|
||||||
|
runs-on: ${{ matrix.runner }}
|
||||||
|
needs: params
|
||||||
|
# Make sure an ID token is created with the necessary permissions to use
|
||||||
|
# GCP's Workload Identity Federation (for service account authentication).
|
||||||
|
permissions:
|
||||||
|
contents: 'read'
|
||||||
|
id-token: 'write'
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||||
|
|
||||||
|
- uses: ./.github/actions/prepare_environment
|
||||||
|
with:
|
||||||
|
target_os: ${{ matrix.os }}
|
||||||
|
is_self_hosted: ${{ matrix.is_self_hosted }}
|
||||||
|
install_test_deps: true
|
||||||
|
|
||||||
|
- name: Install Shells
|
||||||
|
uses: ConorMacBride/install-package@3e7ad059e07782ee54fa35f827df52aae0626f30 # v1
|
||||||
|
if: ${{ matrix.is_self_hosted == false }}
|
||||||
|
with:
|
||||||
|
apt: zsh fish
|
||||||
|
brew: fish bash
|
||||||
|
|
||||||
|
- name: Echo Shells (UNIX)
|
||||||
|
id: echo_shells_unix
|
||||||
|
if: ${{ matrix.os != 'windows' }}
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
if ${{ matrix.os == 'macos' }}; then
|
||||||
|
LATEST_BASH_PATH="$(brew --prefix bash)/bin/bash"
|
||||||
|
echo "latest_bash_path=$LATEST_BASH_PATH" >> $GITHUB_OUTPUT
|
||||||
|
LATEST_BASH_VERSION="$($LATEST_BASH_PATH --version)"
|
||||||
|
echo "::notice title=${{ matrix.name }} Tests - Latest Bash Version::$LATEST_BASH_VERSION"
|
||||||
|
fi
|
||||||
|
|
||||||
|
DEFAULT_BASH_PATH="$(command -pv bash)"
|
||||||
|
DEFAULT_BASH_VERSION="$($DEFAULT_BASH_PATH --version)"
|
||||||
|
echo "default_bash_path=$DEFAULT_BASH_PATH" >> $GITHUB_OUTPUT
|
||||||
|
echo "::notice title=${{ matrix.name }} Tests - Default Bash Version::$DEFAULT_BASH_VERSION"
|
||||||
|
|
||||||
|
FISH_PATH="$(which fish)"
|
||||||
|
echo "fish_path=$FISH_PATH" >> $GITHUB_OUTPUT
|
||||||
|
FISH_VERSION="$($FISH_PATH --version)"
|
||||||
|
echo "::notice title=${{ matrix.name }} Tests - Fish Version::$FISH_VERSION"
|
||||||
|
|
||||||
|
ZSH_VERSION="$(zsh --version)"
|
||||||
|
ZSH_PATH="$(which zsh)"
|
||||||
|
echo "zsh_path=$ZSH_PATH" >> $GITHUB_OUTPUT
|
||||||
|
echo "::notice title=${{ matrix.name }} Tests - Zsh Version::$ZSH_VERSION"
|
||||||
|
|
||||||
|
POWERSHELL_PATH="$(which pwsh)"
|
||||||
|
POWERSHELL_VERSION=$(pwsh -version | awk '{print $2}')
|
||||||
|
echo "powershell_path=$POWERSHELL_PATH" >> $GITHUB_OUTPUT
|
||||||
|
echo "::notice title=${{ matrix.name }} Tests - Powershell Version::$POWERSHELL_VERSION"
|
||||||
|
|
||||||
|
- name: Echo Shells (Windows)
|
||||||
|
id: echo_shells_windows
|
||||||
|
if: ${{ matrix.os == 'windows' }}
|
||||||
|
shell: pwsh
|
||||||
|
run: |
|
||||||
|
$env:POWERSHELL_PATH = (Get-Command pwsh).Source
|
||||||
|
$env:POWERSHELL_VERSION = (Get-Command pwsh).Version.Major
|
||||||
|
Write-Output 'Powershell path:' $env:POWERSHELL_PATH
|
||||||
|
Write-Output 'Powershell version:' $env:POWERSHELL_VERSION
|
||||||
|
Write-Output "powershell_path=$env:POWERSHELL_PATH" >> $env:GITHUB_OUTPUT
|
||||||
|
Write-Output "::notice title=${{ matrix.name }} Tests - Powershell Version::$env:POWERSHELL_VERSION"
|
||||||
|
|
||||||
|
- name: Echo Default Shell
|
||||||
|
id: echo_default_shell
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
if ${{ matrix.os == 'windows' }}; then
|
||||||
|
DEFAULT_SHELL="${{ steps.echo_shells_windows.outputs.powershell_path }}"
|
||||||
|
else
|
||||||
|
DEFAULT_SHELL="${{ steps.echo_shells_unix.outputs.zsh_path }}"
|
||||||
|
fi
|
||||||
|
echo "Using default shell '$DEFAULT_SHELL'"
|
||||||
|
echo "default_shell_path=$DEFAULT_SHELL" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Install cargo nextest
|
||||||
|
if: ${{ matrix.is_self_hosted == false }}
|
||||||
|
uses: taiki-e/install-action@9a29ce630c67077a359246f3e4f84941e05f28b5 # v1
|
||||||
|
with:
|
||||||
|
tool: nextest
|
||||||
|
|
||||||
|
- name: Run ssh-agent for SSH tests
|
||||||
|
if: ${{ matrix.os == 'linux' }}
|
||||||
|
run: |
|
||||||
|
# Create the .ssh dir.
|
||||||
|
mkdir -p ~/.ssh
|
||||||
|
|
||||||
|
# Run the agent.
|
||||||
|
eval "$(ssh-agent -s)"
|
||||||
|
|
||||||
|
# Persist the SSH_AUTH_SOCK and SSH_AGENT_PID for future steps
|
||||||
|
echo "SSH_AUTH_SOCK=$SSH_AUTH_SOCK" >> $GITHUB_ENV
|
||||||
|
echo "SSH_AGENT_PID=$SSH_AGENT_PID" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Set up gcloud authentication for SSH tests
|
||||||
|
uses: google-github-actions/auth@7c6bc770dae815cd3e89ee6cdf493a5fab2cc093 # v3.0.0
|
||||||
|
with:
|
||||||
|
workload_identity_provider: projects/63595664881/locations/global/workloadIdentityPools/github-pool/providers/github-provider
|
||||||
|
service_account: github-ci-workflow@warp-ssh-integration-testing.iam.gserviceaccount.com
|
||||||
|
|
||||||
|
- name: Install gcloud CLI tool
|
||||||
|
uses: google-github-actions/setup-gcloud@aa5489c8933f4cc7a4f7d45035b3b1440c9c10db # v3.0.1
|
||||||
|
with:
|
||||||
|
version: '>= 397.0.0'
|
||||||
|
|
||||||
|
- name: Install vim for richer alt-screen tests
|
||||||
|
if: ${{ matrix.is_self_hosted == false }}
|
||||||
|
uses: ConorMacBride/install-package@3e7ad059e07782ee54fa35f827df52aae0626f30 # v1
|
||||||
|
with:
|
||||||
|
brew: vim
|
||||||
|
apt: vim
|
||||||
|
|
||||||
|
- name: Compile tests
|
||||||
|
run: cargo nextest run ${{ env.WORKSPACE_TEST_ARGS }} ${{ matrix.extra_test_args }} --no-run
|
||||||
|
env:
|
||||||
|
# Attach the GitHub auth token so that requests to the GitHub API
|
||||||
|
# don't get rate limited during compilation (e.g.: retrieving
|
||||||
|
# Sentry SDK binary releases).
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Run unit tests
|
||||||
|
# Unlike later test steps, only run this if previous steps succeed (so
|
||||||
|
# that we don't bother running these first tests if test compilation
|
||||||
|
# fails).
|
||||||
|
if: success()
|
||||||
|
run: cargo nextest run ${{ env.WORKSPACE_TEST_ARGS }} ${{ matrix.extra_test_args }} -E "not package(integration)"
|
||||||
|
env:
|
||||||
|
# Unit tests may end up spawning a shell, but shouldn't care which
|
||||||
|
# shell it is. We'll use zsh, as it has the shortest bootstrap times
|
||||||
|
# and tends to be the most reliable.
|
||||||
|
WARP_SHELL_PATH: ${{ steps.echo_default_shell.outputs.default_shell_path }}
|
||||||
|
|
||||||
|
- name: Upload results of unit tests to trunk.io
|
||||||
|
# Run this step even when the tests fail. Skip if the workflow is cancelled.
|
||||||
|
if: ${{ !cancelled() }}
|
||||||
|
continue-on-error: true
|
||||||
|
uses: trunk-io/analytics-uploader@95a0fb8b29e45b6068304261fb518644b426a803 # v2.0.8
|
||||||
|
with:
|
||||||
|
junit-paths: target/nextest/ci/junit.xml
|
||||||
|
cli-version: 0.12.5
|
||||||
|
org-slug: warp
|
||||||
|
token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||||
|
tags: type=unit
|
||||||
|
variant: ${{ matrix.os }}
|
||||||
|
use-cache: true
|
||||||
|
|
||||||
|
- name: Run shell-agnostic integration tests
|
||||||
|
# Run this step even if a previous test step fails
|
||||||
|
if: matrix.os != 'windows' && (success() || failure())
|
||||||
|
uses: coactions/setup-xvfb@b6b4fcfb9f5a895edadc3bc76318fae0ac17c8b3 # v1
|
||||||
|
with:
|
||||||
|
run: cargo nextest run ${{ env.WORKSPACE_TEST_ARGS }} ${{ matrix.extra_test_args }} -E "package(integration) and not test(shell_integration_tests)"
|
||||||
|
env:
|
||||||
|
# We run shell-agnostic tests against zsh, as it has the shortest
|
||||||
|
# bootstrap times and tends to be the most reliable.
|
||||||
|
WARP_SHELL_PATH: ${{ steps.echo_shells_unix.outputs.zsh_path }}
|
||||||
|
|
||||||
|
- name: Upload results of shell-agnostic integration tests to trunk.io
|
||||||
|
# Run this step even when the tests fail. Skip if the workflow is cancelled.
|
||||||
|
if: ${{ matrix.os != 'windows' && !cancelled() }}
|
||||||
|
continue-on-error: true
|
||||||
|
uses: trunk-io/analytics-uploader@95a0fb8b29e45b6068304261fb518644b426a803 # v2.0.8
|
||||||
|
with:
|
||||||
|
junit-paths: target/nextest/ci/junit.xml
|
||||||
|
cli-version: 0.12.5
|
||||||
|
org-slug: warp
|
||||||
|
token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||||
|
tags: type=integration,category=shell-agnostic
|
||||||
|
variant: ${{ matrix.os }}
|
||||||
|
use-cache: true
|
||||||
|
|
||||||
|
- name: Run shell integration tests against default version of bash
|
||||||
|
# Run this step even if a previous test step fails
|
||||||
|
if: matrix.os != 'windows' && (success() || failure())
|
||||||
|
uses: coactions/setup-xvfb@b6b4fcfb9f5a895edadc3bc76318fae0ac17c8b3 # v1
|
||||||
|
with:
|
||||||
|
run: cargo nextest run ${{ env.WORKSPACE_TEST_ARGS }} ${{ matrix.extra_test_args }} -E "package(integration) and test(shell_integration_tests)"
|
||||||
|
env:
|
||||||
|
WARP_SHELL_PATH: ${{ steps.echo_shells_unix.outputs.default_bash_path }}
|
||||||
|
|
||||||
|
- name: Upload results of shell integration tests against default version of bash to trunk.io
|
||||||
|
# Run this step even when the tests fail. Skip if the workflow is cancelled.
|
||||||
|
if: ${{ matrix.os != 'windows' && !cancelled() }}
|
||||||
|
continue-on-error: true
|
||||||
|
uses: trunk-io/analytics-uploader@95a0fb8b29e45b6068304261fb518644b426a803 # v2.0.8
|
||||||
|
with:
|
||||||
|
junit-paths: target/nextest/ci/junit.xml
|
||||||
|
cli-version: 0.12.5
|
||||||
|
org-slug: warp
|
||||||
|
token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||||
|
tags: type=integration,category=bash-default
|
||||||
|
variant: ${{ matrix.os }}
|
||||||
|
use-cache: true
|
||||||
|
|
||||||
|
- name: Run shell integration tests against latest version of bash
|
||||||
|
# Run this step even if a previous test step fails.
|
||||||
|
# We only run this on MacOS since the default Bash version on Mac
|
||||||
|
# was released in 2007. Most Linux distros ship with a relatively
|
||||||
|
# new (version 5.0+) version of Bash.
|
||||||
|
if: (success() || failure()) && runner.os == 'macos'
|
||||||
|
uses: coactions/setup-xvfb@b6b4fcfb9f5a895edadc3bc76318fae0ac17c8b3 # v1
|
||||||
|
with:
|
||||||
|
run: cargo nextest run ${{ env.WORKSPACE_TEST_ARGS }} ${{ matrix.extra_test_args }} -E "package(integration) and test(shell_integration_tests)"
|
||||||
|
env:
|
||||||
|
WARP_SHELL_PATH: ${{ steps.echo_shells_unix.outputs.latest_bash_path }}
|
||||||
|
|
||||||
|
- name: Upload results of shell integration tests against latest version of bash to trunk.io
|
||||||
|
# Run this step even when the tests fail. Skip if the workflow is cancelled.
|
||||||
|
if: ${{ matrix.os != 'windows' && !cancelled() }}
|
||||||
|
continue-on-error: true
|
||||||
|
uses: trunk-io/analytics-uploader@95a0fb8b29e45b6068304261fb518644b426a803 # v2.0.8
|
||||||
|
with:
|
||||||
|
junit-paths: target/nextest/ci/junit.xml
|
||||||
|
cli-version: 0.12.5
|
||||||
|
org-slug: warp
|
||||||
|
token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||||
|
tags: type=integration,category=bash-latest
|
||||||
|
variant: ${{ matrix.os }}
|
||||||
|
use-cache: true
|
||||||
|
|
||||||
|
- name: Run shell integration tests against fish
|
||||||
|
# Run this step even if a previous test step fails
|
||||||
|
if: matrix.os != 'windows' && (success() || failure())
|
||||||
|
uses: coactions/setup-xvfb@b6b4fcfb9f5a895edadc3bc76318fae0ac17c8b3 # v1
|
||||||
|
with:
|
||||||
|
run: cargo nextest run ${{ env.WORKSPACE_TEST_ARGS }} ${{ matrix.extra_test_args }} -E "package(integration) and test(shell_integration_tests)"
|
||||||
|
env:
|
||||||
|
WARP_SHELL_PATH: ${{ steps.echo_shells_unix.outputs.fish_path }}
|
||||||
|
|
||||||
|
- name: Upload results of shell integration tests against fish to trunk.io
|
||||||
|
# Run this step even when the tests fail. Skip if the workflow is cancelled.
|
||||||
|
if: ${{ matrix.os != 'windows' && !cancelled() }}
|
||||||
|
continue-on-error: true
|
||||||
|
uses: trunk-io/analytics-uploader@95a0fb8b29e45b6068304261fb518644b426a803 # v2.0.8
|
||||||
|
with:
|
||||||
|
junit-paths: target/nextest/ci/junit.xml
|
||||||
|
cli-version: 0.12.5
|
||||||
|
org-slug: warp
|
||||||
|
token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||||
|
tags: type=integration,category=fish
|
||||||
|
variant: ${{ matrix.os }}
|
||||||
|
use-cache: true
|
||||||
|
|
||||||
|
- name: Run shell integration tests against zsh
|
||||||
|
# Run this step even if a previous test step fails
|
||||||
|
if: matrix.os != 'windows' && (success() || failure())
|
||||||
|
uses: coactions/setup-xvfb@b6b4fcfb9f5a895edadc3bc76318fae0ac17c8b3 # v1
|
||||||
|
with:
|
||||||
|
run: cargo nextest run ${{ env.WORKSPACE_TEST_ARGS }} ${{ matrix.extra_test_args }} -E "package(integration) and test(shell_integration_tests)"
|
||||||
|
env:
|
||||||
|
WARP_SHELL_PATH: ${{ steps.echo_shells_unix.outputs.zsh_path }}
|
||||||
|
|
||||||
|
- name: Upload results of shell integration tests against zsh to trunk.io
|
||||||
|
# Run this step even when the tests fail. Skip if the workflow is cancelled.
|
||||||
|
if: ${{ matrix.os != 'windows' && !cancelled() }}
|
||||||
|
continue-on-error: true
|
||||||
|
uses: trunk-io/analytics-uploader@95a0fb8b29e45b6068304261fb518644b426a803 # v2.0.8
|
||||||
|
with:
|
||||||
|
junit-paths: target/nextest/ci/junit.xml
|
||||||
|
cli-version: 0.12.5
|
||||||
|
org-slug: warp
|
||||||
|
token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||||
|
tags: type=integration,category=zsh
|
||||||
|
variant: ${{ matrix.os }}
|
||||||
|
use-cache: true
|
||||||
|
|
||||||
|
- name: Run shell integration tests against powershell
|
||||||
|
# Run this step even if a previous test step fails
|
||||||
|
if: matrix.os != 'windows' && (success() || failure())
|
||||||
|
uses: coactions/setup-xvfb@b6b4fcfb9f5a895edadc3bc76318fae0ac17c8b3 # v1
|
||||||
|
with:
|
||||||
|
run: cargo nextest run ${{ env.WORKSPACE_TEST_ARGS }} ${{ matrix.extra_test_args }} -E "package(integration) and test(shell_integration_tests)"
|
||||||
|
env:
|
||||||
|
WARP_SHELL_PATH: ${{ steps.echo_shells_unix.outputs.powershell_path }}
|
||||||
|
|
||||||
|
- name: Upload results of shell integration tests against powershell to trunk.io
|
||||||
|
# Run this step even when the tests fail. Skip if the workflow is cancelled.
|
||||||
|
if: ${{ matrix.os != 'windows' && !cancelled() }}
|
||||||
|
continue-on-error: true
|
||||||
|
uses: trunk-io/analytics-uploader@95a0fb8b29e45b6068304261fb518644b426a803 # v2.0.8
|
||||||
|
with:
|
||||||
|
junit-paths: target/nextest/ci/junit.xml
|
||||||
|
cli-version: 0.12.5
|
||||||
|
org-slug: warp
|
||||||
|
token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||||
|
tags: type=integration,category=powershell
|
||||||
|
variant: ${{ matrix.os }}
|
||||||
|
use-cache: true
|
||||||
|
|
||||||
|
# Run doctests explicitly, as nextest doesn't yet support running them.
|
||||||
|
# See: https://github.com/nextest-rs/nextest/issues/16
|
||||||
|
- name: Run doc tests
|
||||||
|
# Run this step even if a previous test step fails
|
||||||
|
if: success() || failure()
|
||||||
|
run: cargo test ${{ env.WORKSPACE_TEST_ARGS }} ${{ matrix.extra_test_args }} --doc
|
||||||
|
|
||||||
|
# Run warp_completer tests with the "v2" (completions-on-js) flag enabled. We do this to
|
||||||
|
# ensure that the v2 completions implementation doesn't regress/rot while it's development
|
||||||
|
# is paused.
|
||||||
|
- name: Run completions-on-js tests
|
||||||
|
# Run this step even if a previous test step fails
|
||||||
|
if: matrix.os != 'windows' && (success() || failure())
|
||||||
|
uses: coactions/setup-xvfb@b6b4fcfb9f5a895edadc3bc76318fae0ac17c8b3 # v1
|
||||||
|
with:
|
||||||
|
run: cargo nextest run --locked -p warp_completer --features v2
|
||||||
|
|
||||||
|
# This is longer than we like and temporary until we speed up integration tests
|
||||||
|
# The maximum timeout for the entire job is 6 hours:
|
||||||
|
# https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepstimeout-minutes
|
||||||
|
|
||||||
|
database-migration:
|
||||||
|
name: Database Migration (Diesel)
|
||||||
|
timeout-minutes: 5
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: params
|
||||||
|
if: ${{ needs.params.outputs.affects-database-schema == 'true' }}
|
||||||
|
steps:
|
||||||
|
- name: Checkout sources
|
||||||
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
|
|
||||||
|
- name: Install cargo-binstall
|
||||||
|
uses: cargo-bins/cargo-binstall@dc19f1e48450eefe5a29b8da6c6b00a87d730b37 # v1.18.1
|
||||||
|
|
||||||
|
- name: Install diesel_cli
|
||||||
|
run: |
|
||||||
|
cargo binstall diesel_cli
|
||||||
|
|
||||||
|
- name: Run all migrations on empty file
|
||||||
|
run: |
|
||||||
|
cp crates/persistence/src/schema.rs old-schema.rs
|
||||||
|
diesel migration run --database-url="test.sqlite" --migration-dir="crates/persistence/migrations"
|
||||||
|
|
||||||
|
- name: Regenerate schema and check if it is consistent with the schema at HEAD
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
diff -u old-schema.rs <(diesel print-schema --database-url="test.sqlite")
|
||||||
|
|
||||||
|
lints:
|
||||||
|
name: Formatting + Clippy (${{ matrix.name }})
|
||||||
|
timeout-minutes: 20
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- os: "macos"
|
||||||
|
name: "MacOS"
|
||||||
|
runner: ${{ fromJSON(needs.params.outputs.macos-runner) }}
|
||||||
|
is_self_hosted: ${{ contains(fromJSON(needs.params.outputs.macos-runner), 'self-hosted') }}
|
||||||
|
null_device: "/dev/null"
|
||||||
|
clippy_excludes: "--exclude warp_completer"
|
||||||
|
|
||||||
|
- os: "linux"
|
||||||
|
name: "Linux"
|
||||||
|
# Use a larger (16 core, 64GB ram) runner on Linux to speed up execution time.
|
||||||
|
# https://github.com/warpdotdev/warp-internal/settings/actions/runners
|
||||||
|
runner: ubuntu-latest-large
|
||||||
|
# We don't (yet) have any self-hosted Linux runners.
|
||||||
|
is_self_hosted: false
|
||||||
|
null_device: "/dev/null"
|
||||||
|
clippy_excludes: "--exclude warp_completer"
|
||||||
|
|
||||||
|
- os: "windows"
|
||||||
|
name: "Windows"
|
||||||
|
runner: windows-latest-large
|
||||||
|
# We don't (yet) have any self-hosted Windows runners.
|
||||||
|
is_self_hosted: false
|
||||||
|
null_device: "NUL"
|
||||||
|
clippy_excludes: "--exclude warp_js --exclude command-signatures-v2"
|
||||||
|
runs-on: ${{ matrix.runner }}
|
||||||
|
needs: params
|
||||||
|
# if: ${{ needs.params.outputs.affects-rust-sources == 'true' }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||||
|
|
||||||
|
- uses: ./.github/actions/prepare_environment
|
||||||
|
with:
|
||||||
|
target_os: ${{ matrix.os }}
|
||||||
|
is_self_hosted: ${{ matrix.is_self_hosted }}
|
||||||
|
|
||||||
|
- name: Ensure cargo.lock is up-to-date
|
||||||
|
run:
|
||||||
|
cargo metadata --locked --format-version=1 > ${{ matrix.null_device }} || (echo "::error::Cargo.lock is out-of-date with Cargo.toml. Run 'cargo check' to update." && exit 1)
|
||||||
|
|
||||||
|
- name: Run cargo fmt
|
||||||
|
run: cargo fmt --check
|
||||||
|
|
||||||
|
- name: Run cargo clippy
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
set -e
|
||||||
|
# On Windows, omit --all-features because pprof and related profiling features don't compile.
|
||||||
|
if [ "${{ matrix.os }}" = "windows" ]; then
|
||||||
|
cargo clippy --locked --workspace ${{ matrix.clippy_excludes }} --all-targets --tests -- -D warnings
|
||||||
|
else
|
||||||
|
cargo clippy --locked --workspace ${{ matrix.clippy_excludes }} --all-targets --all-features --tests -- -D warnings
|
||||||
|
# Run clippy on warp_completer with default, rather than all features enabled, because there is
|
||||||
|
# feature-gated logic for the WIP completions-on-js implementation.
|
||||||
|
cargo clippy --locked -p warp_completer --all-targets --tests -- -D warnings
|
||||||
|
fi
|
||||||
|
env:
|
||||||
|
# Attach the GitHub auth token so that requests to the GitHub API
|
||||||
|
# don't get rate limited during compilation (e.g.: retrieving
|
||||||
|
# Sentry SDK binary releases).
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Run clang-format
|
||||||
|
if: ${{ matrix.os == 'macos' }}
|
||||||
|
run: |
|
||||||
|
if [[ ! -d $(brew --prefix clang-format) ]]; then
|
||||||
|
brew install clang-format
|
||||||
|
fi
|
||||||
|
./script/run-clang-format.py -r --extensions 'c,h,cpp,m' ./crates/warpui/src/ ./app/src/
|
||||||
|
|
||||||
|
general-lint:
|
||||||
|
name: Miscellaneous checks
|
||||||
|
timeout-minutes: 10
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: params
|
||||||
|
steps:
|
||||||
|
- name: Checkout sources
|
||||||
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
|
|
||||||
|
- uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
|
||||||
|
with:
|
||||||
|
# Only cache runs on the master branch (caches on feature branches
|
||||||
|
# are not reusable).
|
||||||
|
save-if: ${{ github.ref == 'refs/heads/master' }}
|
||||||
|
|
||||||
|
- name: Install cargo-binstall
|
||||||
|
uses: cargo-bins/cargo-binstall@dc19f1e48450eefe5a29b8da6c6b00a87d730b37 # v1.18.1
|
||||||
|
|
||||||
|
- name: Check licenses for dependencies
|
||||||
|
run: |
|
||||||
|
cargo binstall --force -y cargo-deny
|
||||||
|
# Make sure all of our dependencies use licenses that are compatible with
|
||||||
|
# our usage.
|
||||||
|
cargo deny -L error check licenses
|
||||||
|
|
||||||
|
- name: Check deny.toml / about.toml license sync
|
||||||
|
run: ./script/check_license_config_sync
|
||||||
|
|
||||||
|
- name: Run WGSL formatter
|
||||||
|
run: |
|
||||||
|
# note: this matches the version used in script/install_cargo_test_deps...
|
||||||
|
# if changing here, probably a good idea to change there too to keep dev tools on local ~= CI
|
||||||
|
cargo install --git https://github.com/wgsl-analyzer/wgsl-analyzer --tag "2025-06-28" wgslfmt && \
|
||||||
|
find . -name "*.wgsl" -exec wgslfmt --check {} +
|
||||||
|
|
||||||
|
- name: Run PSScriptAnalyzer (PowerShell Lint)
|
||||||
|
run: ./script/lint_powershell -ci
|
||||||
|
shell: pwsh
|
||||||
|
|
||||||
|
- name: Validate repo-sync markers
|
||||||
|
uses: warpdotdev/repo-sync/actions/validate-markers@main
|
||||||
|
|
||||||
|
wasm-lint:
|
||||||
|
name: Formatting + Clippy (wasm)
|
||||||
|
timeout-minutes: 20
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
runs-on: ${{ fromJSON(needs.params.outputs.wasm-runner) }}
|
||||||
|
needs: params
|
||||||
|
steps:
|
||||||
|
- name: Compute IS_SELF_HOSTED
|
||||||
|
id: is_self_hosted
|
||||||
|
run: |
|
||||||
|
IS_SELF_HOSTED="${{ contains(fromJSON(needs.params.outputs.wasm-runner), 'self-hosted') }}"
|
||||||
|
echo "value=$IS_SELF_HOSTED" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||||
|
|
||||||
|
- uses: ./.github/actions/prepare_environment
|
||||||
|
with:
|
||||||
|
target_os: wasm
|
||||||
|
is_self_hosted: ${{ steps.is_self_hosted.outputs.value }}
|
||||||
|
|
||||||
|
- name: Ensure cargo.lock is up-to-date
|
||||||
|
run:
|
||||||
|
cargo metadata --locked --format-version=1 >/dev/null || (echo "::error::Cargo.lock is out-of-date with Cargo.toml. Run 'cargo check' to update." && exit 1)
|
||||||
|
|
||||||
|
- name: Run cargo fmt
|
||||||
|
run: cargo fmt --check
|
||||||
|
|
||||||
|
- name: Run cargo clippy
|
||||||
|
run: |
|
||||||
|
cargo clippy --locked --target wasm32-unknown-unknown --profile release-wasm-debug_assertions -- -D warnings
|
||||||
|
|
||||||
|
check-release-compilation:
|
||||||
|
name: Verify compilation with release flags (${{ matrix.name }})
|
||||||
|
timeout-minutes: 15
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- os: "macos"
|
||||||
|
name: "MacOS"
|
||||||
|
runner: ${{ fromJSON(needs.params.outputs.macos-runner) }}
|
||||||
|
is_self_hosted: ${{ contains(fromJSON(needs.params.outputs.macos-runner), 'self-hosted') }}
|
||||||
|
null_device: "/dev/null"
|
||||||
|
|
||||||
|
- os: "linux"
|
||||||
|
name: "Linux"
|
||||||
|
# Use a larger (16 core, 64GB ram) runner on Linux to speed up execution time.
|
||||||
|
# https://github.com/warpdotdev/warp-internal/settings/actions/runners
|
||||||
|
runner: ubuntu-latest-large
|
||||||
|
# We don't (yet) have any self-hosted Linux runners.
|
||||||
|
is_self_hosted: false
|
||||||
|
null_device: "/dev/null"
|
||||||
|
|
||||||
|
- os: "windows"
|
||||||
|
name: "Windows"
|
||||||
|
runner: windows-latest-large
|
||||||
|
# We don't (yet) have any self-hosted Windows runners.
|
||||||
|
is_self_hosted: false
|
||||||
|
null_device: "NUL"
|
||||||
|
|
||||||
|
- os: "wasm"
|
||||||
|
name: "wasm"
|
||||||
|
runner: ${{ fromJSON(needs.params.outputs.wasm-runner) }}
|
||||||
|
is_self_hosted: ${{ contains(fromJSON(needs.params.outputs.wasm-runner), 'self-hosted') }}
|
||||||
|
null_device: "/dev/null"
|
||||||
|
runs-on: ${{ matrix.runner }}
|
||||||
|
needs: params
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||||
|
|
||||||
|
- uses: ./.github/actions/prepare_environment
|
||||||
|
with:
|
||||||
|
target_os: ${{ matrix.os }}
|
||||||
|
is_self_hosted: ${{ matrix.is_self_hosted }}
|
||||||
|
|
||||||
|
- name: Verify compilation with release flags
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
if ${{ matrix.os == 'wasm' }}; then
|
||||||
|
./script/wasm/bundle --bin dev --nouniversal --check-only
|
||||||
|
else
|
||||||
|
./script/bundle --bin dev --nouniversal --check-only
|
||||||
|
fi
|
||||||
|
env:
|
||||||
|
# Attach the GitHub auth token so that requests to the GitHub API
|
||||||
|
# don't get rate limited during compilation (e.g.: retrieving
|
||||||
|
# Sentry SDK binary releases).
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
# A final job that collects the results of all of the parallel CI jobs and
|
||||||
|
# makes sure that all of them pass.
|
||||||
|
#
|
||||||
|
# This allows us to tell GitHub that the only required check is this "gather"
|
||||||
|
# job, simplifying the process of adding and removing jobs to the CI
|
||||||
|
# workflow.
|
||||||
|
ci-result:
|
||||||
|
name: Check CI results
|
||||||
|
timeout-minutes: 3
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
# Always run this job, as it needs to check the results of all of the other
|
||||||
|
# jobs. If we skipped CI checks (e.g. on a draft PR), also skip this one.
|
||||||
|
if: ${{ !cancelled() && needs.params.result != 'skipped' }}
|
||||||
|
needs:
|
||||||
|
- params
|
||||||
|
- database-migration
|
||||||
|
- tests
|
||||||
|
- lints
|
||||||
|
- wasm-lint
|
||||||
|
- general-lint
|
||||||
|
- check-release-compilation
|
||||||
|
steps:
|
||||||
|
- name: Check CI results
|
||||||
|
run: |
|
||||||
|
# Convert needs context to JSON and check required jobs
|
||||||
|
echo '${{ toJSON(needs) }}' | jq -r '
|
||||||
|
# Get all jobs except database-migration
|
||||||
|
to_entries |
|
||||||
|
map(select(.key != "database-migration")) |
|
||||||
|
.[] |
|
||||||
|
if .value.result != "success" then
|
||||||
|
"::error::Required job \(.key) failed or was skipped (status: \(.value.result))"
|
||||||
|
else
|
||||||
|
empty
|
||||||
|
end
|
||||||
|
' | {
|
||||||
|
if read -r error; then
|
||||||
|
echo "$error"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Special handling for conditional database-migration job.
|
||||||
|
affects_schema="${{ needs.params.outputs.affects-database-schema }}"
|
||||||
|
migration_result="${{ needs.database-migration.result }}"
|
||||||
|
|
||||||
|
if [[ "$affects_schema" == "true" && "$migration_result" != "success" ]]; then
|
||||||
|
echo "::error::Database migration job failed or was skipped when it should have run (status: $migration_result)"
|
||||||
|
exit 1
|
||||||
|
elif [[ "$affects_schema" != "true" && "$migration_result" != "skipped" ]]; then
|
||||||
|
echo "::error::Database migration job should have been skipped but wasn't (status: $migration_result)"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "All jobs completed successfully!"
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
|
||||||
|
# ======================================================================================
|
||||||
|
# Workflow: Close Stale Fix PRs
|
||||||
|
# ======================================================================================
|
||||||
|
# Usage:
|
||||||
|
# - This workflow runs daily and closes agent-opened CI fix PRs that have been
|
||||||
|
# open for more than 3 days.
|
||||||
|
#
|
||||||
|
# Expected Output:
|
||||||
|
# - Any open PR whose branch matches the oz-agent-fix/run-* convention and that
|
||||||
|
# was opened more than 3 days ago will be closed with an explanatory comment.
|
||||||
|
#
|
||||||
|
# ======================================================================================
|
||||||
|
|
||||||
|
name: Close Stale Fix PRs
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
- cron: '0 12 * * *' # 8am EST
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
close-stale-fix-prs:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Close Stale Fix PRs
|
||||||
|
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
const { owner, repo } = context.repo;
|
||||||
|
const staleCutoffMs = 3 * 24 * 60 * 60 * 1000; // 3 days in milliseconds
|
||||||
|
const now = Date.now();
|
||||||
|
|
||||||
|
// Paginate through all open PRs to find ones matching the branch convention
|
||||||
|
const openPRs = await github.paginate(github.rest.pulls.list, {
|
||||||
|
owner,
|
||||||
|
repo,
|
||||||
|
state: 'open',
|
||||||
|
per_page: 100,
|
||||||
|
});
|
||||||
|
|
||||||
|
const staleFixPRs = openPRs.filter(pr => {
|
||||||
|
const branchMatches = /^oz-agent-fix\/run-/.test(pr.head.ref);
|
||||||
|
const openedAt = new Date(pr.created_at).getTime();
|
||||||
|
const isStale = (now - openedAt) > staleCutoffMs;
|
||||||
|
return branchMatches && isStale;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (staleFixPRs.length === 0) {
|
||||||
|
console.log('No stale fix PRs found.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`Found ${staleFixPRs.length} stale fix PR(s). Closing them...`);
|
||||||
|
|
||||||
|
for (const pr of staleFixPRs) {
|
||||||
|
const ageMs = now - new Date(pr.created_at).getTime();
|
||||||
|
const ageDays = Math.floor(ageMs / (24 * 60 * 60 * 1000));
|
||||||
|
console.log(`Closing PR #${pr.number} (branch: ${pr.head.ref}, open for ${ageDays} day(s)): ${pr.title}`);
|
||||||
|
|
||||||
|
await github.rest.issues.createComment({
|
||||||
|
owner,
|
||||||
|
repo,
|
||||||
|
issue_number: pr.number,
|
||||||
|
body: `Closing this automated CI fix PR because it has been open for more than 3 days (${ageDays} day(s)) without being merged. Please review the underlying CI failures manually if they are still relevant.`,
|
||||||
|
});
|
||||||
|
|
||||||
|
await github.rest.pulls.update({
|
||||||
|
owner,
|
||||||
|
repo,
|
||||||
|
pull_number: pr.number,
|
||||||
|
state: 'closed',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Done.');
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
name: Comment on Unready Assigned Issue (Local)
|
||||||
|
on:
|
||||||
|
issues:
|
||||||
|
types: [assigned]
|
||||||
|
concurrency:
|
||||||
|
group: comment-on-unready-assigned-issue-${{ github.event.issue.number || github.run_id }}
|
||||||
|
cancel-in-progress: false
|
||||||
|
jobs:
|
||||||
|
comment_when_unready:
|
||||||
|
if: github.event.assignee.login == 'oz-agent' && !contains(github.event.issue.labels.*.name, 'ready-to-spec') && !contains(github.event.issue.labels.*.name, 'ready-to-implement')
|
||||||
|
permissions:
|
||||||
|
issues: write
|
||||||
|
uses: warpdotdev/oz-for-oss/.github/workflows/comment-on-unready-assigned-issue.yml@main
|
||||||
|
secrets:
|
||||||
|
OZ_MGMT_GHA_APP_ID: ${{ secrets.OZ_MGMT_GHA_APP_ID }}
|
||||||
|
OZ_MGMT_GHA_PRIVATE_KEY: ${{ secrets.OZ_MGMT_GHA_PRIVATE_KEY }}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
name: Create Implementation from Issue (Local)
|
||||||
|
on:
|
||||||
|
issues:
|
||||||
|
types: [assigned, labeled]
|
||||||
|
issue_comment:
|
||||||
|
types: [created]
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
issue_number:
|
||||||
|
description: Issue number to create an implementation for
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
concurrency:
|
||||||
|
group: create-implementation-issue-${{ github.event.issue.number || inputs.issue_number || github.run_id }}
|
||||||
|
cancel-in-progress: false
|
||||||
|
jobs:
|
||||||
|
# Mention, bot, event-type, and trust gates all live in the reusable
|
||||||
|
# workflow (``create-implementation-from-issue.yml``). This adapter
|
||||||
|
# exists only to subscribe to the GitHub events that can trigger
|
||||||
|
# implementation work (``issues`` assign/label by a maintainer, or a
|
||||||
|
# trusted ``@oz-agent`` issue comment) and delegate them through
|
||||||
|
# ``workflow_call``.
|
||||||
|
create_implementation:
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
issues: write
|
||||||
|
pull-requests: write
|
||||||
|
uses: warpdotdev/oz-for-oss/.github/workflows/create-implementation-from-issue.yml@main
|
||||||
|
with:
|
||||||
|
issue_number: ${{ github.event.inputs.issue_number || '' }}
|
||||||
|
secrets:
|
||||||
|
OZ_MGMT_GHA_APP_ID: ${{ secrets.OZ_MGMT_GHA_APP_ID }}
|
||||||
|
OZ_MGMT_GHA_PRIVATE_KEY: ${{ secrets.OZ_MGMT_GHA_PRIVATE_KEY }}
|
||||||
|
WARP_API_KEY: ${{ secrets.OSS_WARP_API_KEY }}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
name: Create Spec from Issue (Local)
|
||||||
|
on:
|
||||||
|
issues:
|
||||||
|
types: [assigned, labeled]
|
||||||
|
issue_comment:
|
||||||
|
types: [created]
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
issue_number:
|
||||||
|
description: Issue number to create a spec for
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
concurrency:
|
||||||
|
group: create-spec-issue-${{ github.event.issue.number || inputs.issue_number || github.run_id }}
|
||||||
|
cancel-in-progress: false
|
||||||
|
jobs:
|
||||||
|
# Mention, bot, event-type, and trust gates all live in the reusable
|
||||||
|
# workflow (``create-spec-from-issue.yml``). This adapter exists only
|
||||||
|
# to subscribe to the GitHub events that can trigger spec creation
|
||||||
|
# (``issues`` assign/label by a maintainer, or a trusted
|
||||||
|
# ``@oz-agent`` issue comment) and delegate them through
|
||||||
|
# ``workflow_call``.
|
||||||
|
create_spec:
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
issues: write
|
||||||
|
pull-requests: write
|
||||||
|
uses: warpdotdev/oz-for-oss/.github/workflows/create-spec-from-issue.yml@main
|
||||||
|
with:
|
||||||
|
issue_number: ${{ github.event.inputs.issue_number || '' }}
|
||||||
|
secrets:
|
||||||
|
OZ_MGMT_GHA_APP_ID: ${{ secrets.OZ_MGMT_GHA_APP_ID }}
|
||||||
|
OZ_MGMT_GHA_PRIVATE_KEY: ${{ secrets.OZ_MGMT_GHA_PRIVATE_KEY }}
|
||||||
|
WARP_API_KEY: ${{ secrets.OSS_WARP_API_KEY }}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,54 @@
|
|||||||
|
# A workflow to cut a new release candidate (i.e.: .stable_01) on the current
|
||||||
|
# release branch.
|
||||||
|
#
|
||||||
|
# The channel will be automatically determined from the branch name (i.e.: when
|
||||||
|
# run on a stable_release/* branch, it will assume it is building a new "stable"
|
||||||
|
# channel candidate).
|
||||||
|
|
||||||
|
name: Cut New Release Candidate
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
compute_channel:
|
||||||
|
name: Compute release channel
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
outputs:
|
||||||
|
channel: ${{ steps.compute-channel.outputs.channel }}
|
||||||
|
steps:
|
||||||
|
- name: "Compute release channel"
|
||||||
|
id: compute-channel
|
||||||
|
run: |
|
||||||
|
# Check whether the current branch name starts with "some_release/",
|
||||||
|
# and if so, extract the value of "some" into the CHANNEL variable,
|
||||||
|
# discarding the rest of the branch name.
|
||||||
|
CHANNEL="$(echo $GITHUB_REF_NAME | sed -r 's|^(\w+)_release/.*$|\1|')"
|
||||||
|
# If the sed expression doesn't match the input, it will write out the
|
||||||
|
# input, unmodified.
|
||||||
|
if [[ $CHANNEL == $GITHUB_REF_NAME ]]; then
|
||||||
|
echo "::error::Can only create new release candidates on a release branch" && exit 1
|
||||||
|
fi
|
||||||
|
echo "channel=$CHANNEL" >> $GITHUB_OUTPUT
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
create_release_candidate:
|
||||||
|
name: Create Release Candidate
|
||||||
|
uses: ./.github/workflows/create_release.yml
|
||||||
|
needs: compute_channel
|
||||||
|
with:
|
||||||
|
channel: ${{ needs.compute_channel.outputs.channel }}
|
||||||
|
secrets: inherit
|
||||||
|
|
||||||
|
update_channel_versions:
|
||||||
|
name: Update channel_versions.json
|
||||||
|
needs:
|
||||||
|
- create_release_candidate
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Invoke update workflow in channel-versions repository
|
||||||
|
uses: peter-evans/repository-dispatch@28959ce8df70de7be546dd1250a005dd32156697 # v4.0.1
|
||||||
|
with:
|
||||||
|
repository: warpdotdev/channel-versions
|
||||||
|
token: ${{ secrets.CHANNEL_VERSIONS_REPOSITORY_DISPATCH_TOKEN }}
|
||||||
|
event-type: update-channel-versions
|
||||||
|
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
# A workflow to cut new releases (i.e.: .stable_00) for various release
|
||||||
|
# channels.
|
||||||
|
#
|
||||||
|
# Supports cutting releases for nightly release channels, weekly release
|
||||||
|
# channels, or all release channels. Releases run in parallel where able.
|
||||||
|
|
||||||
|
name: Cut New Releases
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
# Run this job every morning at 3am EST.
|
||||||
|
- cron: "0 8 * * *"
|
||||||
|
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
targets:
|
||||||
|
type: choice
|
||||||
|
description: Which releases to build. Nightly = dev; Weekly = preview/stable.
|
||||||
|
options:
|
||||||
|
- nightly
|
||||||
|
- weekly
|
||||||
|
- all
|
||||||
|
|
||||||
|
env:
|
||||||
|
CONFIG_FILE: ".github/workflows/release_configurations.json"
|
||||||
|
# The day of the week when the weekly release should be cut. This is a value
|
||||||
|
# from 1-7, with 1 representing Monday, 2 representing Tuesday, etc.
|
||||||
|
WEEKLY_RELEASE_DAY: 3 # Cut weekly builds on Wednesdays.
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
get_config:
|
||||||
|
name: Get release configuration
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
env:
|
||||||
|
SCHEDULE: ${{ github.event.schedule }}
|
||||||
|
TARGETS: ${{ inputs.targets }}
|
||||||
|
outputs:
|
||||||
|
channels: ${{ steps.get-config.outputs.channels }}
|
||||||
|
steps:
|
||||||
|
- name: Check for master branch
|
||||||
|
run: |
|
||||||
|
[ $GITHUB_REF == "refs/heads/master" ] || (echo "::error::Can only cut new releases on the master branch" && exit 1)
|
||||||
|
shell: bash
|
||||||
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
|
- id: get-config
|
||||||
|
run: |
|
||||||
|
# Check to see if this was auto-run as a cron job. If so, set the
|
||||||
|
# value of TARGETS based on whether the current day of the week
|
||||||
|
# is when we build weekly releases (in addition to nightly ones).
|
||||||
|
if [[ ! -z "$SCHEDULE" ]]; then
|
||||||
|
if [[ "$(date +%u)" == "$WEEKLY_RELEASE_DAY" ]]; then
|
||||||
|
TARGETS="all"
|
||||||
|
else
|
||||||
|
TARGETS="nightly"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ::echo::on
|
||||||
|
if [[ "$TARGETS" == "all" ]]; then
|
||||||
|
TARGETS="$(jq -rc '[.channels[] | .type] | unique' $CONFIG_FILE)"
|
||||||
|
else
|
||||||
|
TARGETS="[\"$TARGETS\"]"
|
||||||
|
fi
|
||||||
|
export CHANNELS="$(jq -rc '[.channels[] | select(.type | inside($ENV.TARGETS)) | .channel]' $CONFIG_FILE)"
|
||||||
|
echo "channels=$CHANNELS" >> $GITHUB_OUTPUT
|
||||||
|
# CHANNELS_INCLUDE_AUTOPUSH=$(jq -rc '[.channels[] | select(.channel | inside($ENV.CHANNELS)) | .is_autopush] | any' $CONFIG_FILE)
|
||||||
|
# echo "channels-include-autopush=$CHANNELS_INCLUDE_AUTOPUSH" >> $GITHUB_OUTPUT
|
||||||
|
echo ::echo::off
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
create_release:
|
||||||
|
name: Create Release
|
||||||
|
uses: ./.github/workflows/create_release.yml
|
||||||
|
needs: get_config
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
channel: ${{ fromJSON(needs.get_config.outputs.channels) }}
|
||||||
|
with:
|
||||||
|
channel: ${{ matrix.channel }}
|
||||||
|
secrets: inherit
|
||||||
|
|
||||||
|
update_channel_versions:
|
||||||
|
name: Update channel_versions.json
|
||||||
|
needs:
|
||||||
|
- create_release
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Invoke update workflow in channel-versions repository
|
||||||
|
uses: peter-evans/repository-dispatch@28959ce8df70de7be546dd1250a005dd32156697 # v4.0.1
|
||||||
|
with:
|
||||||
|
repository: warpdotdev/channel-versions
|
||||||
|
token: ${{ secrets.CHANNEL_VERSIONS_REPOSITORY_DISPATCH_TOKEN }}
|
||||||
|
event-type: update-channel-versions
|
||||||
@@ -0,0 +1,111 @@
|
|||||||
|
# A workflow to delete (soft-delete) a release branch by renaming it.
|
||||||
|
#
|
||||||
|
# The branch will be renamed from its original name to deleted/{original_name}.
|
||||||
|
# For example, stable_release/v0.2025.01.01.00.00 becomes
|
||||||
|
# deleted/stable_release/v0.2025.01.01.00.00.
|
||||||
|
#
|
||||||
|
# This workflow will fail if:
|
||||||
|
# - The branch doesn't start with a release channel prefix
|
||||||
|
# (i.e.: stable_release/, preview_release/, or dev_release/)
|
||||||
|
# - The release has an entry in channel_versions.json (i.e., it's deployed)
|
||||||
|
|
||||||
|
name: Delete Release
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
branch:
|
||||||
|
description: 'The release branch to delete (e.g., stable_release/v0.2025.01.01.00.00.stable)'
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
confirmation:
|
||||||
|
description: 'Type DELETE to confirm'
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
delete_release:
|
||||||
|
name: Delete Release Branch
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Verify confirmation
|
||||||
|
run: |
|
||||||
|
if [[ "${{ inputs.confirmation }}" != "DELETE" ]]; then
|
||||||
|
echo "::error::You must type DELETE to confirm branch deletion. \
|
||||||
|
If you're unsure about deleting, ask a TL in #oncall-client before running this workflow."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
- name: Validate release branch
|
||||||
|
id: validate
|
||||||
|
run: |
|
||||||
|
BRANCH_NAME="${{ inputs.branch }}"
|
||||||
|
|
||||||
|
# Validate branch is a release branch and extract the channel
|
||||||
|
if [[ ! "$BRANCH_NAME" =~ ^(stable|preview|dev)_release/ ]]; then
|
||||||
|
echo "::error::Branch '$BRANCH_NAME' is not a release branch." && exit 1
|
||||||
|
fi
|
||||||
|
CHANNEL="${BASH_REMATCH[1]}"
|
||||||
|
|
||||||
|
# Extract the version base from the branch name
|
||||||
|
# e.g., "stable_release/v0.2025.01.26.12.30.stable" -> "v0.2025.01.26.12.30.stable"
|
||||||
|
VERSION_BASE="$(echo $BRANCH_NAME | sed -r 's|^\w+_release/(.*)$|\1|')"
|
||||||
|
|
||||||
|
echo "branch_name=$BRANCH_NAME" >> $GITHUB_OUTPUT
|
||||||
|
echo "new_branch_name=deleted/$BRANCH_NAME" >> $GITHUB_OUTPUT
|
||||||
|
echo "channel=$CHANNEL" >> $GITHUB_OUTPUT
|
||||||
|
echo "version_base=$VERSION_BASE" >> $GITHUB_OUTPUT
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
- name: Check if release has changelog in channel_versions.json
|
||||||
|
id: check_deployed
|
||||||
|
env:
|
||||||
|
CHANNEL: ${{ steps.validate.outputs.channel }}
|
||||||
|
VERSION_BASE: ${{ steps.validate.outputs.version_base }}
|
||||||
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
run: |
|
||||||
|
# Fetch channel_versions.json from the channel-versions repository
|
||||||
|
CHANNEL_VERSIONS=$(gh api repos/warpdotdev/channel-versions/contents/channel_versions.json --jq '.content' | base64 -d)
|
||||||
|
|
||||||
|
# Fail fast if the channel section is missing from channel_versions.json
|
||||||
|
CHANNEL_EXISTS=$(echo "$CHANNEL_VERSIONS" | jq -r --arg channel "$CHANNEL" 'has("changelogs") and (.changelogs | has($channel))')
|
||||||
|
if [[ "$CHANNEL_EXISTS" != "true" ]]; then
|
||||||
|
echo "::error::channel_versions.json is missing the 'changelogs.$CHANNEL' section. Cannot safely determine if release is deployed."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if any key in the changelogs section for this channel starts with the version base
|
||||||
|
# Keys are like "v0.2025.01.26.12.30.stable_01" and version_base is "v0.2025.01.26.12.30.stable"
|
||||||
|
MATCHING_ENTRIES=$(echo "$CHANNEL_VERSIONS" | jq -r --arg channel "$CHANNEL" --arg version "$VERSION_BASE" \
|
||||||
|
'.changelogs[$channel] | keys[] | select(startswith($version))')
|
||||||
|
|
||||||
|
if [[ -n "$MATCHING_ENTRIES" ]]; then
|
||||||
|
echo "::error::Cannot delete release branch: version '$VERSION_BASE' has changelog entries in channel_versions.json:"
|
||||||
|
echo "$MATCHING_ENTRIES"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "::notice::Release has no changelog in channel_versions.json, safe to delete."
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
|
with:
|
||||||
|
ref: ${{ inputs.branch }}
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Rename branch to deleted/
|
||||||
|
env:
|
||||||
|
BRANCH_NAME: ${{ steps.validate.outputs.branch_name }}
|
||||||
|
NEW_BRANCH_NAME: ${{ steps.validate.outputs.new_branch_name }}
|
||||||
|
run: |
|
||||||
|
echo "Renaming branch '$BRANCH_NAME' to '$NEW_BRANCH_NAME'"
|
||||||
|
|
||||||
|
# Push the current HEAD to the new branch name
|
||||||
|
git push origin "HEAD:refs/heads/$NEW_BRANCH_NAME"
|
||||||
|
|
||||||
|
# Delete the original branch
|
||||||
|
git push origin --delete "$BRANCH_NAME"
|
||||||
|
|
||||||
|
echo "::notice::Successfully renamed branch '$BRANCH_NAME' to '$NEW_BRANCH_NAME'"
|
||||||
|
shell: bash
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
name: "Run Docubot on @docubot mention"
|
||||||
|
on:
|
||||||
|
# These triggers are currently disabled because they cause workflow runs for ALL
|
||||||
|
# comments in the org, even though the job only executes when @docubot is mentioned,
|
||||||
|
# which is noisy.
|
||||||
|
# issue_comment:
|
||||||
|
# types: [created]
|
||||||
|
# pull_request_review_comment:
|
||||||
|
# types: [created]
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
pr_number:
|
||||||
|
description: 'PR number to run Docubot on (leave empty to run on current branch)'
|
||||||
|
required: false
|
||||||
|
type: string
|
||||||
|
prompt:
|
||||||
|
description: Prompt to send to Docubot
|
||||||
|
required: true
|
||||||
|
default: "Analyze this PR for documentation needs"
|
||||||
|
warp_channel:
|
||||||
|
type: choice
|
||||||
|
description: Warp release channel
|
||||||
|
required: true
|
||||||
|
default: dev
|
||||||
|
options:
|
||||||
|
- dev
|
||||||
|
- preview
|
||||||
|
- stable
|
||||||
|
profile_id:
|
||||||
|
description: Warp profile ID
|
||||||
|
required: false
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
issues: write
|
||||||
|
pull-requests: write
|
||||||
|
id-token: write
|
||||||
|
actions: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
run_docubot:
|
||||||
|
if: >
|
||||||
|
(
|
||||||
|
contains(github.event.comment.body, '@docubot') || inputs.prompt
|
||||||
|
)
|
||||||
|
&& github.actor != 'github-actions[bot]'
|
||||||
|
runs-on: namespace-profile-ubuntu-20-04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||||
|
with:
|
||||||
|
# Check out the appropriate branch based on trigger type
|
||||||
|
# TODO: do this in sdk?
|
||||||
|
ref: >
|
||||||
|
${{
|
||||||
|
github.event_name == 'workflow_dispatch'
|
||||||
|
&& (inputs.pr_number != '' && format('refs/pull/{0}/head', inputs.pr_number) || github.ref)
|
||||||
|
|| (
|
||||||
|
github.event_name == 'pull_request_review_comment'
|
||||||
|
&& format('refs/pull/{0}/head', github.event.pull_request.number)
|
||||||
|
|| format('refs/pull/{0}/head', github.event.issue.number)
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
- name: Call Docubot
|
||||||
|
uses: ./.github/actions/docubot
|
||||||
|
with:
|
||||||
|
prompt: ${{ github.event.comment.body || inputs.prompt}}
|
||||||
|
warp_api_key: ${{ secrets.WARP_DEV_API_KEY }}
|
||||||
|
warp_channel: ${{ inputs.warp_channel || 'dev' }}
|
||||||
|
profile_id: ${{ inputs.profile_id || vars.WARP_DEV_API_PROFILE_ID }}
|
||||||
|
github_token: ${{ secrets.PEI_GH_TOKEN_PLS_DEL }}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
name: Enforce PR Issue State Logic
|
||||||
|
on:
|
||||||
|
workflow_call:
|
||||||
|
inputs:
|
||||||
|
pr_number:
|
||||||
|
description: Pull request number to evaluate
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
requester:
|
||||||
|
description: Login of the user whose action triggered enforcement, if any
|
||||||
|
required: false
|
||||||
|
default: ""
|
||||||
|
type: string
|
||||||
|
secrets:
|
||||||
|
OZ_MGMT_GHA_APP_ID:
|
||||||
|
required: true
|
||||||
|
OZ_MGMT_GHA_PRIVATE_KEY:
|
||||||
|
required: true
|
||||||
|
OSS_WARP_API_KEY:
|
||||||
|
required: true
|
||||||
|
outputs:
|
||||||
|
allow_review:
|
||||||
|
description: Whether downstream PR hooks may continue after enforcement.
|
||||||
|
value: ${{ jobs.enforce_issue_state.outputs.allow_review }}
|
||||||
|
jobs:
|
||||||
|
enforce_issue_state:
|
||||||
|
name: Enforce PR issue state
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
issues: write
|
||||||
|
pull-requests: write
|
||||||
|
uses: warpdotdev/oz-for-oss/.github/workflows/enforce-pr-issue-state.yml@main
|
||||||
|
with:
|
||||||
|
pr_number: ${{ inputs.pr_number }}
|
||||||
|
requester: ${{ inputs.requester }}
|
||||||
|
secrets:
|
||||||
|
OZ_MGMT_GHA_APP_ID: ${{ secrets.OZ_MGMT_GHA_APP_ID }}
|
||||||
|
OZ_MGMT_GHA_PRIVATE_KEY: ${{ secrets.OZ_MGMT_GHA_PRIVATE_KEY }}
|
||||||
|
WARP_API_KEY: ${{ secrets.OSS_WARP_API_KEY }}
|
||||||
@@ -0,0 +1,261 @@
|
|||||||
|
name: Feature Flag Cleanup
|
||||||
|
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
- cron: '0 15 * * *' # 10am EST
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
# Default to a read-only token. Jobs that need to push or open PRs widen
|
||||||
|
# permissions explicitly below.
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
analyze:
|
||||||
|
name: Analyze feature flags
|
||||||
|
runs-on: namespace-profile-ubuntu-small
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
pull-requests: read
|
||||||
|
outputs:
|
||||||
|
flag_name: ${{ steps.select_flag.outputs.flag_name }}
|
||||||
|
cargo_flag: ${{ steps.select_flag.outputs.cargo_flag }}
|
||||||
|
reviewers: ${{ steps.select_flag.outputs.reviewers }}
|
||||||
|
analyzed_sha: ${{ steps.record_sha.outputs.analyzed_sha }}
|
||||||
|
artifact_url: ${{ steps.upload_feature_flag_log.outputs.artifact-url }}
|
||||||
|
steps:
|
||||||
|
- name: Check out code
|
||||||
|
uses: namespacelabs/nscloud-checkout-action@938f5d2d403d6224d9a0c0dc559b1dae09c2ede4 # v8.1.1
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Record analyzed commit
|
||||||
|
id: record_sha
|
||||||
|
run: |
|
||||||
|
echo "analyzed_sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
|
- name: Find old feature flags
|
||||||
|
uses: warpdotdev/oz-agent-action@main
|
||||||
|
with:
|
||||||
|
prompt: |
|
||||||
|
Your task is to find and list any feature flags which have been enabled by default for more than 3 months.
|
||||||
|
|
||||||
|
There will always be at least one such flag.
|
||||||
|
|
||||||
|
## Context
|
||||||
|
Feature flags are defined in warp_core/src/features.rs. They're enabled by default in app/Cargo.toml, using the default feature list. The mapping between Cargo features and app features is in the enabled_features function of app/src/lib.rs.
|
||||||
|
|
||||||
|
## Recommended Procedure
|
||||||
|
1. Use `git blame` on the Cargo.toml file to identify when each feature flag was enabled by default.
|
||||||
|
2. Read the enabled_features function to map Cargo features to app features.
|
||||||
|
3. Write features that have been enabled by default for more than 3 months to the output file.
|
||||||
|
|
||||||
|
You may create temporary files to store useful results, and write helper scripts for subtasks. It is not necessary to do everything in one shot.
|
||||||
|
|
||||||
|
## Result Format
|
||||||
|
List the old feature flags in ${{ runner.temp }}/flags_to_cleanup.jsonl, in this format (one flag per line):
|
||||||
|
{ "flag_name": "MyFlag", "cargo_flag": "my_flag", "date_enabled": "2025-02-21", "enabling_commit": "aad3293b8fe" }
|
||||||
|
|
||||||
|
## Reminders
|
||||||
|
* Fields in the output JSONL file have these meanings:
|
||||||
|
* "flag_name" - the name of the FeatureFlag enum variant
|
||||||
|
* "cargo_flag" - the name of the flag used in Cargo.toml
|
||||||
|
* "date_enabled" - the date on which the feature flag was enabled by default
|
||||||
|
* "enabling_commit" - the Git commit in which the feature flag was enabled by default
|
||||||
|
* You are running as part of a GitHub automation and must not commit or push any changes. Another agent will process the file you produce.
|
||||||
|
warp_api_key: ${{ secrets.WARP_API_KEY }}
|
||||||
|
share: team
|
||||||
|
|
||||||
|
- name: Upload feature flag log
|
||||||
|
id: upload_feature_flag_log
|
||||||
|
uses: namespace-actions/upload-artifact@f6ccaacc655aec41b93af180d1d7eef21af862d2 # v1.0.3
|
||||||
|
with:
|
||||||
|
name: flags-to-cleanup
|
||||||
|
path: ${{ runner.temp }}/flags_to_cleanup.jsonl
|
||||||
|
|
||||||
|
# As we test out the workflow, only clean up one flag at a time.
|
||||||
|
# We could also pick several flags, or fan out jobs for every single one.
|
||||||
|
- name: Select a flag to clean up
|
||||||
|
id: select_flag
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ github.token }}
|
||||||
|
run: |
|
||||||
|
cleanup_log="$RUNNER_TEMP/flags_to_cleanup.jsonl"
|
||||||
|
# Skip over flags that are also used to enable/disable functionality at runtime.
|
||||||
|
excluded_flags='["CreatingSharedSessions", "ViewingSharedSessions", "AgentMode"]'
|
||||||
|
|
||||||
|
flag_name=""
|
||||||
|
cargo_flag=""
|
||||||
|
|
||||||
|
# Iterate flags in order (oldest first) and pick the first one that is not
|
||||||
|
# excluded and does not already have an open cleanup PR. Sort by
|
||||||
|
# date_enabled here rather than relying on the analysis agent to emit
|
||||||
|
# the JSONL in any particular order.
|
||||||
|
while IFS= read -r candidate; do
|
||||||
|
[ -z "$candidate" ] && continue
|
||||||
|
candidate_flag="$(jq -r '.flag_name' <<< "$candidate")"
|
||||||
|
|
||||||
|
if [ "$(jq -n --arg flag "$candidate_flag" --argjson excluded "$excluded_flags" '$excluded | index($flag) != null')" = "true" ]; then
|
||||||
|
echo "Skipping $candidate_flag: in excluded list"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
candidate_branch="oz-agent/cleanup-feature-flag-$candidate_flag"
|
||||||
|
existing_pr="$(gh pr list --state open --head "$candidate_branch" --json number --jq '.[0].number')"
|
||||||
|
if [ -n "$existing_pr" ] && [ "$existing_pr" != "null" ]; then
|
||||||
|
echo "Skipping $candidate_flag: open cleanup PR #$existing_pr already exists on $candidate_branch"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
flag_name="$candidate_flag"
|
||||||
|
cargo_flag="$(jq -r '.cargo_flag' <<< "$candidate")"
|
||||||
|
break
|
||||||
|
done < <(jq -c -s 'sort_by(.date_enabled) | .[]' "$cleanup_log")
|
||||||
|
echo "flag_name=$flag_name" >> "$GITHUB_OUTPUT"
|
||||||
|
echo "cargo_flag=$cargo_flag" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
|
echo "Cleaning up $flag_name ($cargo_flag)"
|
||||||
|
|
||||||
|
if [ -z "$flag_name" ] || [ "$flag_name" = "null" ]; then
|
||||||
|
echo "No flag selected for cleanup"
|
||||||
|
echo "reviewers=" >> "$GITHUB_OUTPUT"
|
||||||
|
else
|
||||||
|
enabling_commit="$(jq -r -cs --arg flag "$flag_name" '.[] | select(.flag_name == $flag) | .enabling_commit' "$cleanup_log")"
|
||||||
|
committer_username=""
|
||||||
|
|
||||||
|
# Resolve the GitHub username of the commit author directly via the
|
||||||
|
# commits API. This is more reliable than mapping a commit email to
|
||||||
|
# a GitHub user, since commit emails are often noreply addresses or
|
||||||
|
# otherwise unsearchable.
|
||||||
|
if [ -n "$enabling_commit" ] && [ "$enabling_commit" != "null" ]; then
|
||||||
|
committer_username="$(gh api "repos/${{ github.repository }}/commits/$enabling_commit" --jq '.author.login // ""' 2>/dev/null || echo "")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
default_reviewers="bennavetta"
|
||||||
|
|
||||||
|
# Verify the committer is still a collaborator before assigning as reviewer.
|
||||||
|
if [ -n "$committer_username" ] && gh api "repos/${{ github.repository }}/collaborators/$committer_username" --verbose 2>/dev/null; then
|
||||||
|
echo "Flag originally enabled by $committer_username"
|
||||||
|
echo "reviewers=$committer_username,$default_reviewers" >> "$GITHUB_OUTPUT"
|
||||||
|
elif [ -n "$committer_username" ]; then
|
||||||
|
echo "$committer_username is no longer a collaborator, using fallback"
|
||||||
|
echo "reviewers=$default_reviewers" >> "$GITHUB_OUTPUT"
|
||||||
|
else
|
||||||
|
echo "Could not determine flag committer, using fallback"
|
||||||
|
echo "reviewers=$default_reviewers" >> "$GITHUB_OUTPUT"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
name: Run cleanup agent
|
||||||
|
needs: analyze
|
||||||
|
if: ${{ needs.analyze.outputs.flag_name && needs.analyze.outputs.flag_name != 'null' }}
|
||||||
|
runs-on: namespace-profile-ubuntu-small
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
steps:
|
||||||
|
- name: Check out code
|
||||||
|
uses: namespacelabs/nscloud-checkout-action@938f5d2d403d6224d9a0c0dc559b1dae09c2ede4 # v8.1.1
|
||||||
|
with:
|
||||||
|
ref: ${{ needs.analyze.outputs.analyzed_sha }}
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Setup Rust cache
|
||||||
|
uses: namespacelabs/nscloud-cache-action@a90bb5d4b27522ce881c6e98eebd7d7e6d1653f9 # v1.4.2
|
||||||
|
with:
|
||||||
|
cache: rust
|
||||||
|
|
||||||
|
- name: Clean up the unused feature flag
|
||||||
|
id: cleanup_agent
|
||||||
|
uses: warpdotdev/oz-agent-action@main
|
||||||
|
with:
|
||||||
|
prompt: |
|
||||||
|
The feature flag ${{ needs.analyze.outputs.flag_name }} is now enabled by default.
|
||||||
|
|
||||||
|
Clean up all references to this flag, modifying code to always assume it is enabled. Ensure that the code is correctly formatted and that tests still pass.
|
||||||
|
|
||||||
|
Finally, remove the flag from the `FeatureFlag` enum, and remove the equivalent ${{ needs.analyze.outputs.cargo_flag }} feature in app/Cargo.toml
|
||||||
|
|
||||||
|
You are running as part of a GitHub automation that runs with a read-only token and will package your changes into a patch for a separate job to commit. Do not create a branch, do not commit, do not push, do not create a PR, and do not call `gh`. Leave your changes in the working tree only.
|
||||||
|
share: team
|
||||||
|
warp_api_key: ${{ secrets.WARP_API_KEY }}
|
||||||
|
|
||||||
|
- name: Generate cleanup patch
|
||||||
|
env:
|
||||||
|
ANALYZED_SHA: ${{ needs.analyze.outputs.analyzed_sha }}
|
||||||
|
run: |
|
||||||
|
# If the cleanup agent made any local commits, undo them while keeping
|
||||||
|
# the changes in the working tree, so the resulting patch reflects the
|
||||||
|
# full set of changes relative to the analyzed commit.
|
||||||
|
git reset --mixed "$ANALYZED_SHA"
|
||||||
|
# Stage all changes (modified, new, deleted) so the patch is complete.
|
||||||
|
git add -A
|
||||||
|
git diff --staged --binary > "$RUNNER_TEMP/cleanup.patch"
|
||||||
|
if [ ! -s "$RUNNER_TEMP/cleanup.patch" ]; then
|
||||||
|
echo "Cleanup agent produced no changes" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Upload cleanup patch
|
||||||
|
uses: namespace-actions/upload-artifact@f6ccaacc655aec41b93af180d1d7eef21af862d2 # v1.0.3
|
||||||
|
with:
|
||||||
|
name: cleanup-patch
|
||||||
|
path: ${{ runner.temp }}/cleanup.patch
|
||||||
|
|
||||||
|
create_pr:
|
||||||
|
name: Create cleanup PR
|
||||||
|
needs: [analyze, cleanup]
|
||||||
|
if: ${{ needs.analyze.outputs.flag_name && needs.analyze.outputs.flag_name != 'null' }}
|
||||||
|
runs-on: namespace-profile-ubuntu-small
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
pull-requests: write
|
||||||
|
steps:
|
||||||
|
- name: Check out code
|
||||||
|
uses: namespacelabs/nscloud-checkout-action@938f5d2d403d6224d9a0c0dc559b1dae09c2ede4 # v8.1.1
|
||||||
|
with:
|
||||||
|
ref: ${{ needs.analyze.outputs.analyzed_sha }}
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Download cleanup patch
|
||||||
|
uses: namespace-actions/download-artifact@7cbad919e4b0e09f17e9d6311a444ff002992b5b # v2.0.1
|
||||||
|
with:
|
||||||
|
name: cleanup-patch
|
||||||
|
path: ${{ runner.temp }}
|
||||||
|
|
||||||
|
- name: Apply cleanup patch
|
||||||
|
run: |
|
||||||
|
git apply --binary --whitespace=nowarn "$RUNNER_TEMP/cleanup.patch"
|
||||||
|
|
||||||
|
- name: Create Pull Request
|
||||||
|
id: create_pr
|
||||||
|
uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v8.1.1
|
||||||
|
with:
|
||||||
|
token: ${{ github.token }}
|
||||||
|
base: ${{ github.event.repository.default_branch }}
|
||||||
|
commit-message: "Clean up ${{ needs.analyze.outputs.flag_name }} feature flag"
|
||||||
|
branch: "oz-agent/cleanup-feature-flag-${{ needs.analyze.outputs.flag_name }}"
|
||||||
|
author: "Oz Agent <oz-agent@warp.dev>"
|
||||||
|
delete-branch: true
|
||||||
|
reviewers: ${{ needs.analyze.outputs.reviewers }}
|
||||||
|
title: "Clean up ${{ needs.analyze.outputs.flag_name }} feature flag"
|
||||||
|
body: |
|
||||||
|
Automated cleanup of the `${{ needs.analyze.outputs.flag_name }}` feature flag.
|
||||||
|
|
||||||
|
This PR was generated by the feature flag cleanup workflow.
|
||||||
|
|
||||||
|
- run: |
|
||||||
|
echo "Full feature flag analysis results: ${{ needs.analyze.outputs.artifact_url }}"
|
||||||
|
echo "Pull request created: ${{ steps.create_pr.outputs.pull-request-url }}"
|
||||||
|
|
||||||
|
- name: Send Slack notification
|
||||||
|
uses: slackapi/slack-github-action@af78098f536edbc4de71162a307590698245be95 # v3.0.1
|
||||||
|
if: ${{ steps.create_pr.outputs.pull-request-operation == 'created' }}
|
||||||
|
with:
|
||||||
|
method: chat.postMessage
|
||||||
|
token: ${{ secrets.ACTION_MONITORING_SLACK }}
|
||||||
|
payload: |
|
||||||
|
channel: "#oncall-client"
|
||||||
|
# Follows https://api.slack.com/reference/surfaces/formatting#mentioning-users.
|
||||||
|
# S09SSGE729E is the user group ID for @eng-warp-3.
|
||||||
|
text: "🧹 New feature flag cleanup PR for `${{ needs.analyze.outputs.flag_name }}`: ${{ steps.create_pr.outputs.pull-request-url }}! <!subteam^S09SSGE729E> please review\nSee logs at https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
# Potentially run this workflow whenever the master branch changes in,
|
||||||
|
# order to update the cache if necessary.
|
||||||
|
- master
|
||||||
|
paths:
|
||||||
|
# We only need to run this if there were changes to files that affect
|
||||||
|
# the cache key.
|
||||||
|
- 'Cargo.lock'
|
||||||
|
- '**/Cargo.toml'
|
||||||
|
- '.cargo/config.toml'
|
||||||
|
- 'rust-toolchain.toml'
|
||||||
|
# Run once per day to ensure the cache is populated even if we haven't
|
||||||
|
# modified Cargo.lock or Cargo.toml in a while.
|
||||||
|
schedule:
|
||||||
|
- cron: "0 8 * * 1-5" # Run every weekday at 3am EST.
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
name: Populate Build Cache
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
populate_build_cache:
|
||||||
|
name: CI
|
||||||
|
uses: ./.github/workflows/ci.yml
|
||||||
|
secrets: inherit
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
{
|
||||||
|
"documentation": "See the README.md file in this directory.",
|
||||||
|
"channels": [
|
||||||
|
{
|
||||||
|
"channel": "dev",
|
||||||
|
"type": "nightly",
|
||||||
|
"is_prerelease": true,
|
||||||
|
"is_autopush": true,
|
||||||
|
"release_base_name": "Dev Release",
|
||||||
|
"release_body_text": "Nightly Warp Dev release",
|
||||||
|
"sentry_project": "warp-client-dev",
|
||||||
|
"sentry_environment": "dev_release",
|
||||||
|
"changelog_slack_channel": "#dev-beta-changelogs",
|
||||||
|
"gcs_cache_control_value": "private, max-age=604800, immutable",
|
||||||
|
"web_gcs_bucket_prefix": "warp-server-staging"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"channel": "preview",
|
||||||
|
"type": "weekly",
|
||||||
|
"is_prerelease": false,
|
||||||
|
"is_autopush": false,
|
||||||
|
"release_base_name": "Preview Release",
|
||||||
|
"release_body_text": "Warp Preview release",
|
||||||
|
"sentry_project": "warp-client-beta-stable",
|
||||||
|
"sentry_environment": "preview_release",
|
||||||
|
"changelog_slack_channel": "#dev-beta-changelogs",
|
||||||
|
"gcs_cache_control_value": "public, max-age=604800, immutable",
|
||||||
|
"web_gcs_bucket_prefix": "warp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"channel": "stable",
|
||||||
|
"type": "weekly",
|
||||||
|
"is_prerelease": false,
|
||||||
|
"is_autopush": false,
|
||||||
|
"release_base_name": "Stable Release",
|
||||||
|
"release_body_text": "Warp Stable release",
|
||||||
|
"sentry_project": "warp-client-beta-stable",
|
||||||
|
"sentry_environment": "stable_release",
|
||||||
|
"changelog_slack_channel": "#release",
|
||||||
|
"gcs_cache_control_value": "public, max-age=604800, immutable",
|
||||||
|
"web_gcs_bucket_prefix": "warp"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
name: Remove Stale Issue Labels on Plan Approved (Local)
|
||||||
|
on:
|
||||||
|
pull_request_target:
|
||||||
|
types: [labeled]
|
||||||
|
concurrency:
|
||||||
|
group: remove-stale-labels-${{ github.event.pull_request.number || github.run_id }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
jobs:
|
||||||
|
remove_stale_labels:
|
||||||
|
if: github.event.label.name == 'plan-approved'
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
issues: write
|
||||||
|
pull-requests: read
|
||||||
|
uses: warpdotdev/oz-for-oss/.github/workflows/remove-stale-issue-labels-on-plan-approved.yml@main
|
||||||
|
with:
|
||||||
|
pr_number: ${{ github.event.pull_request.number }}
|
||||||
|
secrets:
|
||||||
|
OZ_MGMT_GHA_APP_ID: ${{ secrets.OZ_MGMT_GHA_APP_ID }}
|
||||||
|
OZ_MGMT_GHA_PRIVATE_KEY: ${{ secrets.OZ_MGMT_GHA_PRIVATE_KEY }}
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
name: Repo Sync
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [master]
|
||||||
|
pull_request:
|
||||||
|
types: [closed, opened, synchronize, edited, labeled]
|
||||||
|
branches: [master]
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
action:
|
||||||
|
description: "Which action to run"
|
||||||
|
required: true
|
||||||
|
type: choice
|
||||||
|
options: [sync, escalation]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
sync:
|
||||||
|
if: github.event_name == 'push' || inputs.action == 'sync'
|
||||||
|
uses: warpdotdev/repo-sync/.github/workflows/sync.yml@main
|
||||||
|
with:
|
||||||
|
repo_sync_ref: main
|
||||||
|
app_id: ${{ vars.REPO_SYNC_APP_ID }}
|
||||||
|
public_repo: warpdotdev/warp
|
||||||
|
private_repo: warpdotdev/warp-internal
|
||||||
|
secrets:
|
||||||
|
app_private_key: ${{ secrets.REPO_SYNC_APP_PRIVATE_KEY }}
|
||||||
|
warp_api_key: ${{ secrets.WARP_API_KEY }}
|
||||||
|
|
||||||
|
restack:
|
||||||
|
# Run the restack workflow on PRs for `repo-sync/`-prefixed branches
|
||||||
|
# when they merge or are labelled with `repo-sync:needs-restack`.
|
||||||
|
if: >-
|
||||||
|
github.event_name == 'pull_request' &&
|
||||||
|
startsWith(github.event.pull_request.head.ref, 'repo-sync/') &&
|
||||||
|
(
|
||||||
|
github.event.pull_request.merged == true ||
|
||||||
|
(github.event.action == 'labeled' && github.event.label.name == 'repo-sync:needs-restack')
|
||||||
|
)
|
||||||
|
uses: warpdotdev/repo-sync/.github/workflows/restack.yml@main
|
||||||
|
with:
|
||||||
|
repo_sync_ref: main
|
||||||
|
app_id: ${{ vars.REPO_SYNC_APP_ID }}
|
||||||
|
public_repo: warpdotdev/warp
|
||||||
|
private_repo: warpdotdev/warp-internal
|
||||||
|
secrets:
|
||||||
|
app_private_key: ${{ secrets.REPO_SYNC_APP_PRIVATE_KEY }}
|
||||||
|
warp_api_key: ${{ secrets.WARP_API_KEY }}
|
||||||
|
|
||||||
|
approve:
|
||||||
|
if: >-
|
||||||
|
github.event_name == 'pull_request' &&
|
||||||
|
github.event.action != 'closed' &&
|
||||||
|
startsWith(github.event.pull_request.head.ref, 'repo-sync/')
|
||||||
|
uses: warpdotdev/repo-sync/.github/workflows/approve.yml@main
|
||||||
|
with:
|
||||||
|
repo_sync_ref: main
|
||||||
|
approver_app_id: ${{ vars.REPO_SYNC_APPROVER_APP_ID }}
|
||||||
|
public_repo: warpdotdev/warp
|
||||||
|
private_repo: warpdotdev/warp-internal
|
||||||
|
secrets:
|
||||||
|
approver_app_private_key: ${{ secrets.REPO_SYNC_APPROVER_APP_PRIVATE_KEY }}
|
||||||
|
|
||||||
|
escalation:
|
||||||
|
if: inputs.action == 'escalation'
|
||||||
|
uses: warpdotdev/repo-sync/.github/workflows/escalation.yml@main
|
||||||
|
with:
|
||||||
|
repo_sync_ref: main
|
||||||
|
app_id: ${{ vars.REPO_SYNC_APP_ID }}
|
||||||
|
public_repo: warpdotdev/warp
|
||||||
|
private_repo: warpdotdev/warp-internal
|
||||||
|
escalate_to: "@oncall-client-primary"
|
||||||
|
escalate_after: "30m"
|
||||||
|
secrets:
|
||||||
|
app_private_key: ${{ secrets.REPO_SYNC_APP_PRIVATE_KEY }}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
name: Respond to PR Comment (Local)
|
||||||
|
on:
|
||||||
|
issue_comment:
|
||||||
|
types: [created]
|
||||||
|
pull_request_review_comment:
|
||||||
|
types: [created]
|
||||||
|
pull_request_review:
|
||||||
|
types: [submitted]
|
||||||
|
jobs:
|
||||||
|
# Mention, bot, event-type, and trust gates all live in the reusable
|
||||||
|
# workflow (``respond-to-pr-comment.yml``). This adapter exists only
|
||||||
|
# to subscribe to the three GitHub events that can carry an
|
||||||
|
# ``@oz-agent`` mention on a PR and delegate them through
|
||||||
|
# ``workflow_call``.
|
||||||
|
respond:
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
issues: write
|
||||||
|
pull-requests: write
|
||||||
|
uses: warpdotdev/oz-for-oss/.github/workflows/respond-to-pr-comment.yml@main
|
||||||
|
secrets:
|
||||||
|
OZ_MGMT_GHA_APP_ID: ${{ secrets.OZ_MGMT_GHA_APP_ID }}
|
||||||
|
OZ_MGMT_GHA_PRIVATE_KEY: ${{ secrets.OZ_MGMT_GHA_PRIVATE_KEY }}
|
||||||
|
WARP_API_KEY: ${{ secrets.OSS_WARP_API_KEY }}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
name: Respond to Triaged Issue Comment (Local)
|
||||||
|
on:
|
||||||
|
issue_comment:
|
||||||
|
types: [created]
|
||||||
|
concurrency:
|
||||||
|
group: respond-to-triaged-issue-comment-${{ github.event.comment.id || github.run_id }}
|
||||||
|
cancel-in-progress: false
|
||||||
|
jobs:
|
||||||
|
# Mention, bot, event-type, and trust gates all live in the reusable
|
||||||
|
# workflow (``respond-to-triaged-issue-comment.yml``). This adapter
|
||||||
|
# exists only to subscribe to the GitHub event that can carry an
|
||||||
|
# ``@oz-agent`` mention on a triaged issue and delegate it through
|
||||||
|
# ``workflow_call``.
|
||||||
|
respond_inline:
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
issues: write
|
||||||
|
uses: warpdotdev/oz-for-oss/.github/workflows/respond-to-triaged-issue-comment.yml@main
|
||||||
|
secrets:
|
||||||
|
OZ_MGMT_GHA_APP_ID: ${{ secrets.OZ_MGMT_GHA_APP_ID }}
|
||||||
|
OZ_MGMT_GHA_PRIVATE_KEY: ${{ secrets.OZ_MGMT_GHA_PRIVATE_KEY }}
|
||||||
|
WARP_API_KEY: ${{ secrets.OSS_WARP_API_KEY }}
|
||||||
@@ -0,0 +1,141 @@
|
|||||||
|
name: Review Pull Request
|
||||||
|
on:
|
||||||
|
workflow_call:
|
||||||
|
inputs:
|
||||||
|
pr_number:
|
||||||
|
description: Pull request number to review
|
||||||
|
required: false
|
||||||
|
default: ""
|
||||||
|
type: string
|
||||||
|
trigger_source:
|
||||||
|
description: Source that requested the review
|
||||||
|
required: false
|
||||||
|
default: ""
|
||||||
|
type: string
|
||||||
|
requester:
|
||||||
|
description: Login of the user who requested the review, if any
|
||||||
|
required: false
|
||||||
|
default: ""
|
||||||
|
type: string
|
||||||
|
focus:
|
||||||
|
description: Optional extra focus guidance for the review
|
||||||
|
required: false
|
||||||
|
default: ""
|
||||||
|
type: string
|
||||||
|
comment_id:
|
||||||
|
description: Issue comment ID to react to for slash-command reviews
|
||||||
|
required: false
|
||||||
|
default: ""
|
||||||
|
type: string
|
||||||
|
secrets:
|
||||||
|
OZ_MGMT_GHA_APP_ID:
|
||||||
|
required: true
|
||||||
|
OZ_MGMT_GHA_PRIVATE_KEY:
|
||||||
|
required: true
|
||||||
|
OSS_WARP_API_KEY:
|
||||||
|
required: true
|
||||||
|
pull_request_target:
|
||||||
|
types:
|
||||||
|
- opened
|
||||||
|
- ready_for_review
|
||||||
|
- review_requested
|
||||||
|
- labeled
|
||||||
|
jobs:
|
||||||
|
resolve:
|
||||||
|
runs-on: ubuntu-slim
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
outputs:
|
||||||
|
should_run: ${{ steps.resolve.outputs.should_run }}
|
||||||
|
pr_number: ${{ steps.resolve.outputs.pr_number }}
|
||||||
|
trigger_source: ${{ steps.resolve.outputs.trigger_source }}
|
||||||
|
requester: ${{ steps.resolve.outputs.requester }}
|
||||||
|
focus: ${{ steps.resolve.outputs.focus }}
|
||||||
|
comment_id: ${{ steps.resolve.outputs.comment_id }}
|
||||||
|
skip_reason: ${{ steps.resolve.outputs.skip_reason }}
|
||||||
|
steps:
|
||||||
|
- name: Checkout repo
|
||||||
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||||
|
- name: Resolve review context
|
||||||
|
id: resolve
|
||||||
|
env:
|
||||||
|
INPUT_PR_NUMBER: ${{ inputs.pr_number || '' }}
|
||||||
|
INPUT_TRIGGER_SOURCE: ${{ inputs.trigger_source || '' }}
|
||||||
|
INPUT_REQUESTER: ${{ inputs.requester || '' }}
|
||||||
|
INPUT_FOCUS: ${{ inputs.focus || '' }}
|
||||||
|
INPUT_COMMENT_ID: ${{ inputs.comment_id || '' }}
|
||||||
|
GITHUB_ACTOR_LOGIN: ${{ github.actor }}
|
||||||
|
run: |
|
||||||
|
python - <<'PY'
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
from pathlib import Path
|
||||||
|
event = json.loads(Path(os.environ["GITHUB_EVENT_PATH"]).read_text())
|
||||||
|
event_name = os.environ.get("GITHUB_EVENT_NAME", "")
|
||||||
|
input_pr_number = os.environ.get("INPUT_PR_NUMBER", "").strip()
|
||||||
|
pr = event.get("pull_request") or {}
|
||||||
|
head_ref = ((pr.get("head") or {}).get("ref") or "").strip()
|
||||||
|
action = (event.get("action") or "").strip()
|
||||||
|
requested_reviewer = ((event.get("requested_reviewer") or {}).get("login") or "").strip()
|
||||||
|
label_name = ((event.get("label") or {}).get("name") or "").strip()
|
||||||
|
has_pr_hooks = Path(".github/workflows/pr-hooks.yml").exists()
|
||||||
|
trigger_source = os.environ.get("INPUT_TRIGGER_SOURCE", "").strip() or event_name
|
||||||
|
requester = os.environ.get("INPUT_REQUESTER", "").strip() or os.environ.get("GITHUB_ACTOR_LOGIN", "")
|
||||||
|
focus = os.environ.get("INPUT_FOCUS", "")
|
||||||
|
comment_id = os.environ.get("INPUT_COMMENT_ID", "")
|
||||||
|
pr_number = input_pr_number or str(pr.get("number") or "")
|
||||||
|
matches_direct_trigger = (
|
||||||
|
(action == "opened" and not pr.get("draft", False))
|
||||||
|
or action == "ready_for_review"
|
||||||
|
or (action == "review_requested" and requested_reviewer == "oz-agent")
|
||||||
|
or (action == "labeled" and label_name == "oz-review")
|
||||||
|
)
|
||||||
|
if input_pr_number:
|
||||||
|
should_run = True
|
||||||
|
elif head_ref.startswith("cherrypick"):
|
||||||
|
should_run = False
|
||||||
|
elif has_pr_hooks and event_name == "pull_request_target":
|
||||||
|
should_run = False
|
||||||
|
else:
|
||||||
|
should_run = matches_direct_trigger and bool(pr_number)
|
||||||
|
with open(os.environ["GITHUB_OUTPUT"], "a", encoding="utf-8") as fh:
|
||||||
|
fh.write(f"should_run={'true' if should_run else 'false'}\n")
|
||||||
|
fh.write(f"pr_number={pr_number}\n")
|
||||||
|
fh.write(f"trigger_source={trigger_source}\n")
|
||||||
|
fh.write(f"requester={requester}\n")
|
||||||
|
fh.write("focus<<__EOF__\n")
|
||||||
|
fh.write(focus)
|
||||||
|
fh.write("\n__EOF__\n")
|
||||||
|
fh.write(f"comment_id={comment_id}\n")
|
||||||
|
if has_pr_hooks and event_name == "pull_request_target" and not input_pr_number:
|
||||||
|
fh.write("skip_reason=pr-hooks-present\n")
|
||||||
|
elif head_ref.startswith("cherrypick"):
|
||||||
|
fh.write("skip_reason=cherrypick-branch\n")
|
||||||
|
elif not should_run:
|
||||||
|
fh.write("skip_reason=event-not-enabled\n")
|
||||||
|
PY
|
||||||
|
skip_direct_trigger:
|
||||||
|
needs: resolve
|
||||||
|
if: needs.resolve.outputs.should_run != 'true' && needs.resolve.outputs.skip_reason == 'pr-hooks-present'
|
||||||
|
runs-on: ubuntu-slim
|
||||||
|
steps:
|
||||||
|
- name: Explain skip
|
||||||
|
run: echo "PR review orchestration skipped because .github/workflows/pr-hooks.yml is present."
|
||||||
|
review_pr:
|
||||||
|
needs: resolve
|
||||||
|
if: needs.resolve.outputs.should_run == 'true'
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
pull-requests: write
|
||||||
|
issues: write
|
||||||
|
uses: warpdotdev/oz-for-oss/.github/workflows/review-pull-request.yml@main
|
||||||
|
with:
|
||||||
|
pr_number: ${{ needs.resolve.outputs.pr_number }}
|
||||||
|
trigger_source: ${{ needs.resolve.outputs.trigger_source }}
|
||||||
|
requester: ${{ needs.resolve.outputs.requester }}
|
||||||
|
focus: ${{ needs.resolve.outputs.focus }}
|
||||||
|
comment_id: ${{ needs.resolve.outputs.comment_id }}
|
||||||
|
secrets:
|
||||||
|
OZ_MGMT_GHA_APP_ID: ${{ secrets.OZ_MGMT_GHA_APP_ID }}
|
||||||
|
OZ_MGMT_GHA_PRIVATE_KEY: ${{ secrets.OZ_MGMT_GHA_PRIVATE_KEY }}
|
||||||
|
WARP_API_KEY: ${{ secrets.OSS_WARP_API_KEY }}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
# Fails a required status check if a pull request from one `repo-sync/*`
|
||||||
|
# branch targets another `repo-sync/*` branch. Combined with branch
|
||||||
|
# protection on `repo-sync/**` requiring this check, this prevents one sync
|
||||||
|
# PR from being merged into another sync PR's branch (which would bypass the
|
||||||
|
# restacking flow and corrupt the sync stack).
|
||||||
|
#
|
||||||
|
# PRs from non-`repo-sync/*` branches into `repo-sync/*` branches (e.g. for
|
||||||
|
# conflict resolution) are intentionally allowed. Direct pushes to
|
||||||
|
# `repo-sync/*` branches are also unaffected, since only the `pull_request`
|
||||||
|
# trigger is used here.
|
||||||
|
|
||||||
|
name: Sync PR Checks
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
types: [opened, reopened, edited, synchronize, ready_for_review]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
check:
|
||||||
|
name: Verify PR base is the default branch
|
||||||
|
runs-on: ubuntu-slim
|
||||||
|
steps:
|
||||||
|
- name: Fail if merging a repo-sync branch into another repo-sync branch
|
||||||
|
env:
|
||||||
|
BASE_REF: ${{ github.base_ref }}
|
||||||
|
HEAD_REF: ${{ github.head_ref }}
|
||||||
|
run: |
|
||||||
|
if [[ "$BASE_REF" == repo-sync/* && "$HEAD_REF" == repo-sync/* ]]; then
|
||||||
|
echo "::error::repo-sync/* PRs cannot be merged into other repo-sync/* branches."
|
||||||
|
echo "Wait for repo-sync to retarget this PR to the default branch before merging."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "Base branch '$BASE_REF' and head branch '$HEAD_REF' are OK."
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
name: Triage New Issues (Local)
|
||||||
|
on:
|
||||||
|
issues:
|
||||||
|
types: [opened]
|
||||||
|
issue_comment:
|
||||||
|
types: [created]
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
issue_number:
|
||||||
|
description: Optional issue number to triage immediately
|
||||||
|
required: false
|
||||||
|
default: ''
|
||||||
|
type: string
|
||||||
|
lookback_minutes:
|
||||||
|
description: Minutes of issue history to scan when no issue number is provided
|
||||||
|
required: false
|
||||||
|
default: '60'
|
||||||
|
type: string
|
||||||
|
concurrency:
|
||||||
|
group: triage-new-issues-${{ github.event.issue.number || inputs.issue_number || github.run_id }}
|
||||||
|
cancel-in-progress: false
|
||||||
|
jobs:
|
||||||
|
triage_issues:
|
||||||
|
# A needs-info reply by the original reporter triggers re-triage
|
||||||
|
# even if it mentions @oz-agent, because the respond-to-triaged
|
||||||
|
# workflow handles explicit mentions on triaged issues separately.
|
||||||
|
if: |
|
||||||
|
(
|
||||||
|
github.event_name != 'issue_comment' &&
|
||||||
|
!contains(github.event.issue.labels.*.name, 'triaged') &&
|
||||||
|
!contains(github.event.issue.labels.*.name, 'ready-to-spec') &&
|
||||||
|
!contains(github.event.issue.labels.*.name, 'ready-to-implement')
|
||||||
|
) || (
|
||||||
|
github.event_name == 'issue_comment' &&
|
||||||
|
!github.event.issue.pull_request &&
|
||||||
|
github.event.comment.user.type != 'Bot' &&
|
||||||
|
!endsWith(github.event.comment.user.login, '[bot]') &&
|
||||||
|
(
|
||||||
|
(
|
||||||
|
contains(github.event.comment.body, '@oz-agent') &&
|
||||||
|
!contains(github.event.issue.labels.*.name, 'triaged')
|
||||||
|
) ||
|
||||||
|
(
|
||||||
|
contains(github.event.issue.labels.*.name, 'needs-info') &&
|
||||||
|
github.event.comment.user.login == github.event.issue.user.login &&
|
||||||
|
!contains(github.event.comment.body, '@oz-agent')
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
issues: write
|
||||||
|
uses: warpdotdev/oz-for-oss/.github/workflows/triage-new-issues.yml@main
|
||||||
|
with:
|
||||||
|
issue_number: ${{ github.event.issue.number || github.event.inputs.issue_number || '' }}
|
||||||
|
lookback_minutes: ${{ github.event.inputs.lookback_minutes || '60' }}
|
||||||
|
secrets:
|
||||||
|
OZ_MGMT_GHA_APP_ID: ${{ secrets.OZ_MGMT_GHA_APP_ID }}
|
||||||
|
OZ_MGMT_GHA_PRIVATE_KEY: ${{ secrets.OZ_MGMT_GHA_PRIVATE_KEY }}
|
||||||
|
WARP_API_KEY: ${{ secrets.OSS_WARP_API_KEY }}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
name: Trigger Implementation on Plan Approved (Local)
|
||||||
|
on:
|
||||||
|
pull_request_target:
|
||||||
|
types: [labeled]
|
||||||
|
concurrency:
|
||||||
|
group: trigger-impl-plan-approved-${{ github.event.pull_request.number || github.run_id }}
|
||||||
|
cancel-in-progress: false
|
||||||
|
jobs:
|
||||||
|
trigger_implementation:
|
||||||
|
if: >-
|
||||||
|
github.event.label.name == 'plan-approved' &&
|
||||||
|
github.event.pull_request.state == 'open'
|
||||||
|
name: Trigger implementation for approved plan
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
issues: write
|
||||||
|
pull-requests: write
|
||||||
|
uses: warpdotdev/oz-for-oss/.github/workflows/trigger-implementation-on-plan-approved.yml@main
|
||||||
|
secrets:
|
||||||
|
OZ_MGMT_GHA_APP_ID: ${{ secrets.OZ_MGMT_GHA_APP_ID }}
|
||||||
|
OZ_MGMT_GHA_PRIVATE_KEY: ${{ secrets.OZ_MGMT_GHA_PRIVATE_KEY }}
|
||||||
|
WARP_API_KEY: ${{ secrets.OSS_WARP_API_KEY }}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
name: Update Dedupe Skill (Local)
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
- cron: '30 9 * * 1' # Every Monday at 09:30 UTC
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
lookback_days:
|
||||||
|
description: Number of days to look back for closed-as-duplicate signals
|
||||||
|
required: false
|
||||||
|
default: '7'
|
||||||
|
type: string
|
||||||
|
jobs:
|
||||||
|
update_dedupe:
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
pull-requests: write
|
||||||
|
uses: warpdotdev/oz-for-oss/.github/workflows/update-dedupe.yml@main
|
||||||
|
with:
|
||||||
|
lookback_days: ${{ github.event.inputs.lookback_days || '7' }}
|
||||||
|
secrets:
|
||||||
|
OZ_MGMT_GHA_APP_ID: ${{ secrets.OZ_MGMT_GHA_APP_ID }}
|
||||||
|
OZ_MGMT_GHA_PRIVATE_KEY: ${{ secrets.OZ_MGMT_GHA_PRIVATE_KEY }}
|
||||||
|
WARP_API_KEY: ${{ secrets.OSS_WARP_API_KEY }}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
name: Update PR Review Skill (Local)
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
- cron: '0 9 * * 1' # Every Monday at 09:00 UTC
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
lookback_days:
|
||||||
|
description: Number of days to look back for PR feedback
|
||||||
|
required: false
|
||||||
|
default: '7'
|
||||||
|
type: string
|
||||||
|
jobs:
|
||||||
|
update_pr_review:
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
pull-requests: write
|
||||||
|
uses: warpdotdev/oz-for-oss/.github/workflows/update-pr-review.yml@main
|
||||||
|
with:
|
||||||
|
lookback_days: ${{ github.event.inputs.lookback_days || '7' }}
|
||||||
|
secrets:
|
||||||
|
OZ_MGMT_GHA_APP_ID: ${{ secrets.OZ_MGMT_GHA_APP_ID }}
|
||||||
|
OZ_MGMT_GHA_PRIVATE_KEY: ${{ secrets.OZ_MGMT_GHA_PRIVATE_KEY }}
|
||||||
|
WARP_API_KEY: ${{ secrets.OSS_WARP_API_KEY }}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
name: Update Triage Skill (Local)
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
- cron: '15 9 * * 1' # Every Monday at 09:15 UTC
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
lookback_days:
|
||||||
|
description: Number of days to look back for triage feedback
|
||||||
|
required: false
|
||||||
|
default: '7'
|
||||||
|
type: string
|
||||||
|
jobs:
|
||||||
|
update_triage:
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
pull-requests: write
|
||||||
|
uses: warpdotdev/oz-for-oss/.github/workflows/update-triage.yml@main
|
||||||
|
with:
|
||||||
|
lookback_days: ${{ github.event.inputs.lookback_days || '7' }}
|
||||||
|
secrets:
|
||||||
|
OZ_MGMT_GHA_APP_ID: ${{ secrets.OZ_MGMT_GHA_APP_ID }}
|
||||||
|
OZ_MGMT_GHA_PRIVATE_KEY: ${{ secrets.OZ_MGMT_GHA_PRIVATE_KEY }}
|
||||||
|
WARP_API_KEY: ${{ secrets.OSS_WARP_API_KEY }}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
name: Verify PR Comment (Local)
|
||||||
|
on:
|
||||||
|
issue_comment:
|
||||||
|
types: [created]
|
||||||
|
jobs:
|
||||||
|
# Slash-command parsing, bot gating, and trust admission live in the
|
||||||
|
# reusable workflow. This local adapter only subscribes to PR issue
|
||||||
|
# comments and delegates through ``workflow_call``.
|
||||||
|
verify:
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
issues: write
|
||||||
|
pull-requests: write
|
||||||
|
uses: warpdotdev/oz-for-oss/.github/workflows/verify-pr-comment.yml@main
|
||||||
|
secrets:
|
||||||
|
OZ_MGMT_GHA_APP_ID: ${{ secrets.OZ_MGMT_GHA_APP_ID }}
|
||||||
|
OZ_MGMT_GHA_PRIVATE_KEY: ${{ secrets.OZ_MGMT_GHA_PRIVATE_KEY }}
|
||||||
|
WARP_API_KEY: ${{ secrets.OSS_WARP_API_KEY }}
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
|
||||||
|
# ======================================================================================
|
||||||
|
# Workflow: Cleanup Fix PRs
|
||||||
|
# ======================================================================================
|
||||||
|
# Usage:
|
||||||
|
# - This workflow cleans up PRs generated by Fix Failing Checks
|
||||||
|
#
|
||||||
|
# Expected Output:
|
||||||
|
# - The workflow should close all PRs created by the github-actions bot that are based on a branch with the auto-fix-ci label after it closes
|
||||||
|
#
|
||||||
|
# ======================================================================================
|
||||||
|
|
||||||
|
name: Cleanup Fix PRs
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
types: [closed]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
close-fix-prs:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: contains(github.event.pull_request.labels.*.name, 'auto-fix-ci')
|
||||||
|
steps:
|
||||||
|
- name: Close Dependent Fix PRs
|
||||||
|
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
const { pull_request: pr, repository } = context.payload;
|
||||||
|
const { owner, repo } = context.repo;
|
||||||
|
|
||||||
|
const closedBranch = pr.head.ref;
|
||||||
|
console.log(`Original PR #${pr.number} closed. Head ref was: ${closedBranch}`);
|
||||||
|
|
||||||
|
// Find open PRs that target the closed PR's branch
|
||||||
|
const { data: openPRs } = await github.rest.pulls.list({
|
||||||
|
owner,
|
||||||
|
repo,
|
||||||
|
state: 'open',
|
||||||
|
base: closedBranch
|
||||||
|
});
|
||||||
|
|
||||||
|
if (openPRs.length === 0) {
|
||||||
|
console.log('No open fix PRs found targeting this branch.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`Found ${openPRs.length} open PRs targeting ${closedBranch}. Closing them...`);
|
||||||
|
|
||||||
|
for (const openPR of openPRs) {
|
||||||
|
if (openPR.user.login !== 'github-actions[bot]') {
|
||||||
|
console.log(`Skipping PR #${openPR.number} created by ${openPR.user.login} (not github-actions[bot])`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`Closing PR #${openPR.number}: ${openPR.title}`);
|
||||||
|
|
||||||
|
// Comment on the fix PR
|
||||||
|
await github.rest.issues.createComment({
|
||||||
|
owner,
|
||||||
|
repo,
|
||||||
|
issue_number: openPR.number,
|
||||||
|
body: `Closing this fix PR because the parent PR #${pr.number} has been merged.`
|
||||||
|
});
|
||||||
|
|
||||||
|
// Close the fix PR
|
||||||
|
await github.rest.pulls.update({
|
||||||
|
owner,
|
||||||
|
repo,
|
||||||
|
pull_number: openPR.number,
|
||||||
|
state: 'closed'
|
||||||
|
});
|
||||||
|
}
|
||||||
+57
@@ -0,0 +1,57 @@
|
|||||||
|
app/Carthage
|
||||||
|
app/frameworks/default/Carthage
|
||||||
|
app/frameworks/dev/Carthage
|
||||||
|
app/frameworks/**/*.xcframework
|
||||||
|
/target
|
||||||
|
/app/target
|
||||||
|
/warp.xcworkspace
|
||||||
|
.idea/
|
||||||
|
.DS_Store
|
||||||
|
*.icloud
|
||||||
|
app/src/server/graphql/schema/generated
|
||||||
|
crates/command-signatures-v2/js/build
|
||||||
|
crates/command-signatures-v2/js/node_modules
|
||||||
|
|
||||||
|
# For testing changes to the channel versions file
|
||||||
|
channel_versions_test.json
|
||||||
|
|
||||||
|
# Don't include any CPU profiling output by accident.
|
||||||
|
profile.pb
|
||||||
|
|
||||||
|
# Don't include any files that we write for testing purposes.
|
||||||
|
crates/warp_files/test_data/test_write
|
||||||
|
|
||||||
|
# Don't include fonts downloaded by the script used to generate font fallback code.
|
||||||
|
script/font_fallback/downloaded_fonts
|
||||||
|
|
||||||
|
# Don't include the generated Windows installer
|
||||||
|
script/windows/Output
|
||||||
|
.aider*
|
||||||
|
|
||||||
|
# temporary vim files. Source: https://github.com/github/gitignore/blob/main/Global/Vim.gitignore
|
||||||
|
*~
|
||||||
|
[._]*.s[a-v][a-z]
|
||||||
|
!*.svg # keep svg files
|
||||||
|
[._]*.sw[a-p]
|
||||||
|
[._]s[a-rt-v][a-z]
|
||||||
|
[._]ss[a-gi-z]
|
||||||
|
[._]sw[a-p]
|
||||||
|
|
||||||
|
# Don't include the PTY recording.
|
||||||
|
warp.pty.recording
|
||||||
|
|
||||||
|
# Don't include captured frames generated locally.
|
||||||
|
frame_capture_*.png
|
||||||
|
|
||||||
|
# Don't include history file generated by migrations
|
||||||
|
app/src/persistence/schema.rs.orig
|
||||||
|
|
||||||
|
# Don't include personal Claude Code settings
|
||||||
|
.claude/settings.local.json
|
||||||
|
|
||||||
|
# Tab drag development notes
|
||||||
|
pr_cleanup.md
|
||||||
|
desired_behavior.md
|
||||||
|
|
||||||
|
# Don't include the python cache for bundled skills.
|
||||||
|
__pycache__/
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"mcpServers": {
|
||||||
|
"github": {
|
||||||
|
"command": "npx",
|
||||||
|
"args": [
|
||||||
|
"-y",
|
||||||
|
"@modelcontextprotocol/server-github"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
edition = "2018"
|
||||||
Vendored
+10
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"recommendations": [
|
||||||
|
"GraphQL.vscode-graphql",
|
||||||
|
"GraphQL.vscode-graphql-syntax",
|
||||||
|
"ms-vscode.PowerShell",
|
||||||
|
"rust-lang.rust-analyzer",
|
||||||
|
"tamasfe.even-better-toml",
|
||||||
|
"vadimcn.vscode-lldb",
|
||||||
|
],
|
||||||
|
}
|
||||||
Vendored
+151
@@ -0,0 +1,151 @@
|
|||||||
|
{
|
||||||
|
// Use IntelliSense to learn about possible attributes.
|
||||||
|
// Hover to view descriptions of existing attributes.
|
||||||
|
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"type": "lldb",
|
||||||
|
"request": "attach",
|
||||||
|
"name": "Debug already-running process",
|
||||||
|
"pid": "${command:pickMyProcess}",
|
||||||
|
"sourceLanguages": ["rust"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "lldb",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "Debug executable 'warp'",
|
||||||
|
"cargo": {
|
||||||
|
"args": ["build", "--bin=warp"],
|
||||||
|
"filter": {
|
||||||
|
"name": "warp",
|
||||||
|
"kind": "bin"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"args": [],
|
||||||
|
"cwd": "${workspaceFolder}",
|
||||||
|
"sourceLanguages": ["rust"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "lldb",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "Debug MacOS executable",
|
||||||
|
"preLaunchTask": "build_warplocal",
|
||||||
|
"program": "${workspaceFolder}/target/debug/bundle/osx/WarpLocal.app/Contents/MacOS/warp",
|
||||||
|
"args": [],
|
||||||
|
"cwd": "${workspaceFolder}",
|
||||||
|
"sourceLanguages": ["rust"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "lldb",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "Debug executable 'warp' with fast_dev",
|
||||||
|
"cargo": {
|
||||||
|
"args": ["build", "--bin=warp", "--features=fast_dev"],
|
||||||
|
"filter": {
|
||||||
|
"name": "warp",
|
||||||
|
"kind": "bin"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"args": [],
|
||||||
|
"cwd": "${workspaceFolder}",
|
||||||
|
"sourceLanguages": ["rust"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "lldb",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "Debug executable 'warp' with with_local_server",
|
||||||
|
"cargo": {
|
||||||
|
"args": ["build", "--bin=warp", "--features=with_local_server"],
|
||||||
|
"filter": {
|
||||||
|
"name": "warp",
|
||||||
|
"kind": "bin"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"args": [],
|
||||||
|
"cwd": "${workspaceFolder}",
|
||||||
|
"sourceLanguages": ["rust"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "lldb",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "Debug MacOS executable local session sharing server",
|
||||||
|
"preLaunchTask": "build_warplocal_localsessionsharing",
|
||||||
|
"program": "${workspaceFolder}/target/debug/bundle/osx/WarpLocal.app/Contents/MacOS/warp",
|
||||||
|
"args": [],
|
||||||
|
"cwd": "${workspaceFolder}",
|
||||||
|
"sourceLanguages": ["rust"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "lldb",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "Debug MacOS executable with fast_dev",
|
||||||
|
"env": {
|
||||||
|
"RUST_BACKTRACE": "1"
|
||||||
|
},
|
||||||
|
"preLaunchTask": "build_warplocal_fastdev",
|
||||||
|
"program": "${workspaceFolder}/target/debug/bundle/osx/WarpLocal.app/Contents/MacOS/warp",
|
||||||
|
"args": [],
|
||||||
|
"cwd": "${workspaceFolder}",
|
||||||
|
"sourceLanguages": ["rust"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "lldb",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "Debug MacOS executable with fast_dev + local session sharing server",
|
||||||
|
"preLaunchTask": "build_warplocal_fastdev_localsessionsharing",
|
||||||
|
"program": "${workspaceFolder}/target/debug/bundle/osx/WarpLocal.app/Contents/MacOS/warp",
|
||||||
|
"args": [],
|
||||||
|
"cwd": "${workspaceFolder}",
|
||||||
|
"sourceLanguages": ["rust"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "lldb",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "Debug unit tests in executable 'warp'",
|
||||||
|
"cargo": {
|
||||||
|
"args": ["test", "--no-run", "--bin=warp", "--package=warp"],
|
||||||
|
"filter": {
|
||||||
|
"name": "warp",
|
||||||
|
"kind": "bin"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"args": [],
|
||||||
|
"cwd": "${workspaceFolder}",
|
||||||
|
"sourceLanguages": ["rust"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "lldb",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "Debug integration tests in executable 'integration'",
|
||||||
|
"cargo": {
|
||||||
|
"args": ["build", "--bin=integration", "--package=warp"],
|
||||||
|
"env": {
|
||||||
|
"WARPUI_USE_REAL_DISPLAY_IN_INTEGRATION_TESTS": "1"
|
||||||
|
},
|
||||||
|
"filter": {
|
||||||
|
"name": "integration",
|
||||||
|
"kind": "bin"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"args": ["test_waterfall_input"],
|
||||||
|
"cwd": "${workspaceFolder}",
|
||||||
|
"sourceLanguages": ["rust"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "lldb",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "Debug unit tests in library 'warpui'",
|
||||||
|
"cargo": {
|
||||||
|
"args": ["test", "--no-run", "--lib", "--package=warpui"],
|
||||||
|
"filter": {
|
||||||
|
"name": "warpui",
|
||||||
|
"kind": "lib"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"args": [],
|
||||||
|
"cwd": "${workspaceFolder}",
|
||||||
|
"sourceLanguages": ["rust"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
Vendored
+35
@@ -0,0 +1,35 @@
|
|||||||
|
{
|
||||||
|
"[markdown]": {
|
||||||
|
"editor.wordWrap": "wordWrapColumn",
|
||||||
|
"editor.wordWrapColumn": 80,
|
||||||
|
"editor.quickSuggestions": {
|
||||||
|
"comments": "off",
|
||||||
|
"strings": "off",
|
||||||
|
"other": "off"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"[powershell]": {
|
||||||
|
"editor.semanticHighlighting.enabled": true,
|
||||||
|
"editor.formatOnSave": true
|
||||||
|
},
|
||||||
|
"files.insertFinalNewline": true,
|
||||||
|
"files.trimFinalNewlines": true,
|
||||||
|
"files.associations": {
|
||||||
|
"**/ui/src/platform/mac/**/*.h": "objective-c"
|
||||||
|
},
|
||||||
|
"graphql-config.load.rootDir": "graphql/",
|
||||||
|
"rust-analyzer.cargo.targetDir": true,
|
||||||
|
"powershell.codeFormatting.addWhitespaceAroundPipe": true,
|
||||||
|
"powershell.codeFormatting.alignPropertyValuePairs": false,
|
||||||
|
"powershell.codeFormatting.newLineAfterCloseBrace": false,
|
||||||
|
"powershell.codeFormatting.pipelineIndentationStyle": "IncreaseIndentationForFirstPipeline",
|
||||||
|
"powershell.codeFormatting.trimWhitespaceAroundPipe": true,
|
||||||
|
"powershell.codeFormatting.useConstantStrings": true,
|
||||||
|
"powershell.codeFormatting.whitespaceAfterSeparator": true,
|
||||||
|
"powershell.codeFormatting.whitespaceAroundOperator": true,
|
||||||
|
"powershell.codeFormatting.whitespaceBeforeOpenBrace": true,
|
||||||
|
"powershell.codeFormatting.whitespaceBeforeOpenParen": true,
|
||||||
|
"powershell.codeFormatting.whitespaceInsideBrace": true,
|
||||||
|
"powershell.scriptAnalysis.enable": true,
|
||||||
|
"powershell.scriptAnalysis.settingsPath": "./.PSScriptAnalyzerSettings.psd1",
|
||||||
|
}
|
||||||
Vendored
+46
@@ -0,0 +1,46 @@
|
|||||||
|
{
|
||||||
|
"version": "2.0.0",
|
||||||
|
"tasks": [
|
||||||
|
{
|
||||||
|
"label": "Watch",
|
||||||
|
"group": "build",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "cargo watch",
|
||||||
|
"problemMatcher": "$rustc-watch",
|
||||||
|
"isBackground": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "cargo",
|
||||||
|
"command": "build",
|
||||||
|
"problemMatcher": [
|
||||||
|
"$rustc"
|
||||||
|
],
|
||||||
|
"group": {
|
||||||
|
"kind": "build",
|
||||||
|
"isDefault": true
|
||||||
|
},
|
||||||
|
"label": "rust: cargo build"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "shell",
|
||||||
|
"command": "./script/run --dont-open",
|
||||||
|
"label": "build_warplocal"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "shell",
|
||||||
|
"command": "./script/run --dont-open --features fast_dev",
|
||||||
|
"label": "build_warplocal_fastdev"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "shell",
|
||||||
|
"command": "./script/run --dont-open --features fast_dev,with_local_session_sharing_server",
|
||||||
|
"label": "build_warplocal_fastdev_localsessionsharing"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "shell",
|
||||||
|
"command": "./script/run --dont-open --features with_local_session_sharing_server",
|
||||||
|
"label": "build_warplocal_localsessionsharing"
|
||||||
|
}
|
||||||
|
// TODO: If adding more configs that just add feature flags, consider argument passing from launch.json to tasks.json
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,266 @@
|
|||||||
|
---
|
||||||
|
name: integration-test-video
|
||||||
|
description: Run Warp integration tests with screenshot and video capture, including event overlay annotations for mouse and keyboard input. Use this whenever the user wants to record an integration test, collect screenshots from a test, review generated recording artifacts, or author a test that captures video for debugging or demos.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Integration Test Video Recording
|
||||||
|
|
||||||
|
Use this skill when working with Warp's integration test recording pipeline on this branch.
|
||||||
|
|
||||||
|
The relevant implementation lives in:
|
||||||
|
- `integration/src/bin/integration.rs`
|
||||||
|
- `integration/src/test/video_recording.rs`
|
||||||
|
- `integration/tests/integration/ui_tests.rs`
|
||||||
|
- `ui/src/integration/driver.rs`
|
||||||
|
- `ui/src/integration/step.rs`
|
||||||
|
- `ui/src/integration/video_recorder.rs`
|
||||||
|
- `ui/src/integration/artifacts.rs`
|
||||||
|
- `ui/src/integration/overlay.rs`
|
||||||
|
|
||||||
|
## Command to invoke a test
|
||||||
|
|
||||||
|
For a single manually-invoked recording test, prefer the integration binary:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
WARPUI_USE_REAL_DISPLAY_IN_INTEGRATION_TESTS=1 \
|
||||||
|
cargo run -p integration --bin integration -- test_video_recording
|
||||||
|
```
|
||||||
|
|
||||||
|
That is the command shown by the sample test in `integration/src/test/video_recording.rs`.
|
||||||
|
|
||||||
|
If you want the driver to auto-record a test or set of tests, add `WARP_INTEGRATION_TEST_VIDEO`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
WARPUI_USE_REAL_DISPLAY_IN_INTEGRATION_TESTS=1 \
|
||||||
|
WARP_INTEGRATION_TEST_VIDEO=test_video_recording \
|
||||||
|
cargo run -p integration --bin integration -- test_video_recording
|
||||||
|
```
|
||||||
|
|
||||||
|
For broader integration test runs, the same env vars work with the normal test runner:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
WARPUI_USE_REAL_DISPLAY_IN_INTEGRATION_TESTS=1 \
|
||||||
|
WARP_INTEGRATION_TEST_VIDEO=test_foo,test_bar \
|
||||||
|
cargo nextest run --no-fail-fast --workspace test_foo
|
||||||
|
```
|
||||||
|
|
||||||
|
## Environment variables
|
||||||
|
|
||||||
|
### `WARPUI_USE_REAL_DISPLAY_IN_INTEGRATION_TESTS`
|
||||||
|
- Set this to `1` when you need real frame capture.
|
||||||
|
- Use it for screenshot/video workflows and manual visual verification.
|
||||||
|
- Without a real display, expect recording workflows to be incomplete or unusable.
|
||||||
|
|
||||||
|
### `WARP_INTEGRATION_TEST_VIDEO`
|
||||||
|
This is the main env var that controls driver-managed video recording in `ui/src/integration/driver.rs`.
|
||||||
|
|
||||||
|
Behavior:
|
||||||
|
- Unset or empty: auto-recording is disabled.
|
||||||
|
- `1` or `all`: auto-record every test in the run.
|
||||||
|
- Comma-separated test names: auto-record only those tests.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Record every test in the run
|
||||||
|
WARP_INTEGRATION_TEST_VIDEO=all
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Record only specific tests
|
||||||
|
WARP_INTEGRATION_TEST_VIDEO=test_foo,test_bar
|
||||||
|
```
|
||||||
|
|
||||||
|
Important nuance:
|
||||||
|
- You do not need `WARP_INTEGRATION_TEST_VIDEO` if the test itself explicitly calls `with_start_recording()` and `with_stop_recording()`.
|
||||||
|
- Use the env var when you want whole-test recording without changing the test code.
|
||||||
|
|
||||||
|
### `WARP_INTEGRATION_TEST_ARTIFACTS_DIR`
|
||||||
|
This controls the root artifact directory used by `TestArtifacts` in `ui/src/integration/artifacts.rs`.
|
||||||
|
|
||||||
|
If unset, artifacts go under:
|
||||||
|
|
||||||
|
```text
|
||||||
|
$TMPDIR/warp_integration_test_artifacts
|
||||||
|
```
|
||||||
|
|
||||||
|
Each run gets a timestamped directory:
|
||||||
|
|
||||||
|
```text
|
||||||
|
<artifacts_root>/<test_name>/<timestamp>/
|
||||||
|
```
|
||||||
|
|
||||||
|
This is the main directory to inspect for screenshots, logs, and the final `recording.mp4`.
|
||||||
|
|
||||||
|
### `WARP_INTEGRATION_TEST_VIDEO_DIR`
|
||||||
|
This env var exists in `ui/src/integration/video_recorder.rs` as the lower-level recorder output root helper, defaulting to:
|
||||||
|
|
||||||
|
```text
|
||||||
|
$TMPDIR/warp_integration_video_captures
|
||||||
|
```
|
||||||
|
|
||||||
|
On this branch, the normal integration driver flow writes the finalized video into the test artifacts directory instead, so `WARP_INTEGRATION_TEST_ARTIFACTS_DIR` is the one you usually care about when reviewing results.
|
||||||
|
|
||||||
|
## How to specify which tests to record
|
||||||
|
|
||||||
|
There are two modes:
|
||||||
|
|
||||||
|
### 1. Record in test code
|
||||||
|
Use `TestStep::with_start_recording()` and `TestStep::with_stop_recording()` inside the test itself. This is best when you only want to capture a specific span of the test.
|
||||||
|
|
||||||
|
### 2. Record from the environment
|
||||||
|
Set `WARP_INTEGRATION_TEST_VIDEO` to:
|
||||||
|
- `all`
|
||||||
|
- `1`
|
||||||
|
- or a comma-separated list like `test_a,test_b`
|
||||||
|
|
||||||
|
This starts recording at the beginning of matching tests and writes the video when the test completes.
|
||||||
|
|
||||||
|
## How overlays work
|
||||||
|
|
||||||
|
There is no separate overlay env var on this branch.
|
||||||
|
|
||||||
|
Overlay annotations are produced from the input events the test dispatches while recording is active. The overlay pipeline is implemented in `ui/src/integration/overlay.rs`, and the event capture hooks live in `ui/src/integration/step.rs`.
|
||||||
|
|
||||||
|
To get useful overlays in the final video, drive the test with APIs that emit mouse and keyboard events, such as:
|
||||||
|
- `with_event(...)`
|
||||||
|
- `with_event_fn(...)`
|
||||||
|
- `with_click_on_saved_position(...)`
|
||||||
|
- `with_keystrokes(...)`
|
||||||
|
|
||||||
|
Overlay types currently exercised by the sample test:
|
||||||
|
- mouse click indicators
|
||||||
|
- drag trails
|
||||||
|
- keyboard shortcut pills
|
||||||
|
|
||||||
|
In practice:
|
||||||
|
- Mouse down / drag / mouse up events create click and drag overlays.
|
||||||
|
- KeyDown events create keyboard overlay pills.
|
||||||
|
- If a test only records frames and never dispatches relevant input events, the resulting video will not show these annotations.
|
||||||
|
|
||||||
|
## How to write a test that takes screenshots
|
||||||
|
|
||||||
|
Use `TestStep::with_take_screenshot("filename.png")`.
|
||||||
|
|
||||||
|
Example pattern:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
TestStep::new("Take screenshot after bootstrap")
|
||||||
|
.with_take_screenshot("after_bootstrap.png")
|
||||||
|
```
|
||||||
|
|
||||||
|
The screenshot request is stored during the step and written by the driver after the step renders. The PNG lands in the test's timestamped artifacts directory.
|
||||||
|
|
||||||
|
## How to write a test that records video
|
||||||
|
|
||||||
|
### Minimum pattern
|
||||||
|
1. Use `Builder::new().with_real_display()`.
|
||||||
|
2. Add a step with `with_start_recording()`.
|
||||||
|
3. Run the actions/events you want captured.
|
||||||
|
4. Add a step with `with_stop_recording()`.
|
||||||
|
|
||||||
|
Example shape:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
Builder::new()
|
||||||
|
.with_real_display()
|
||||||
|
.with_step(TestStep::new("Start recording").with_start_recording())
|
||||||
|
.with_step(/* actions and events */)
|
||||||
|
.with_step(TestStep::new("Stop recording").with_stop_recording())
|
||||||
|
```
|
||||||
|
|
||||||
|
### For overlay-friendly recordings
|
||||||
|
Prefer explicit UI-driving steps that emit mouse and key events:
|
||||||
|
- click with `with_click_on_saved_position(...)`
|
||||||
|
- dispatch raw mouse events with `with_event(...)` / `with_event_fn(...)`
|
||||||
|
- send keyboard shortcuts with `with_keystrokes(...)`
|
||||||
|
|
||||||
|
For drag overlays, send a sequence like:
|
||||||
|
- `LeftMouseDown`
|
||||||
|
- one or more `LeftMouseDragged`
|
||||||
|
- `LeftMouseUp`
|
||||||
|
|
||||||
|
### Optional validation
|
||||||
|
It is reasonable to add an `with_on_finish(...)` hook that checks for expected artifacts such as:
|
||||||
|
- `recording.mp4`
|
||||||
|
- `recording.log`
|
||||||
|
- screenshot PNGs
|
||||||
|
|
||||||
|
The sample test does exactly that.
|
||||||
|
|
||||||
|
## Where the video assets go
|
||||||
|
|
||||||
|
The normal output location is:
|
||||||
|
|
||||||
|
```text
|
||||||
|
${WARP_INTEGRATION_TEST_ARTIFACTS_DIR:-$TMPDIR/warp_integration_test_artifacts}/<test_name>/<timestamp>/
|
||||||
|
```
|
||||||
|
|
||||||
|
Common artifacts in that directory:
|
||||||
|
- `recording.mp4`
|
||||||
|
- `recording.log`
|
||||||
|
- any screenshots requested with `with_take_screenshot(...)`
|
||||||
|
|
||||||
|
For `test_video_recording`, the sample test expects:
|
||||||
|
- `after_bootstrap.png`
|
||||||
|
- `after_commands.png`
|
||||||
|
- `recording.mp4`
|
||||||
|
- `recording.log`
|
||||||
|
|
||||||
|
If MP4 encoding fails during finalization, the recorder falls back to per-frame PNGs in a sibling directory like:
|
||||||
|
|
||||||
|
```text
|
||||||
|
recording_frames/
|
||||||
|
```
|
||||||
|
|
||||||
|
with files such as:
|
||||||
|
|
||||||
|
```text
|
||||||
|
recording_0000.png
|
||||||
|
```
|
||||||
|
|
||||||
|
## How to review the assets
|
||||||
|
|
||||||
|
1. Open the latest timestamped artifact directory for the test.
|
||||||
|
2. Review `recording.mp4` first to confirm:
|
||||||
|
- the UI state is correct
|
||||||
|
- recording actually started and stopped in the intended window
|
||||||
|
- overlay annotations appear at the right moments
|
||||||
|
3. Review any PNG screenshots captured by the test.
|
||||||
|
4. Check `recording.log` if the output looks incomplete or suspicious.
|
||||||
|
5. If `recording.mp4` is missing, look for fallback frame PNGs.
|
||||||
|
|
||||||
|
When summarizing results for the user, include the exact artifact directory path.
|
||||||
|
|
||||||
|
## Sample test for video recording
|
||||||
|
|
||||||
|
The sample manual test is `test_video_recording`.
|
||||||
|
|
||||||
|
It is:
|
||||||
|
- registered in `integration/src/bin/integration.rs`
|
||||||
|
- listed in `integration/tests/integration/ui_tests.rs`
|
||||||
|
- implemented in `integration/src/test/video_recording.rs`
|
||||||
|
|
||||||
|
Run it with:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
WARPUI_USE_REAL_DISPLAY_IN_INTEGRATION_TESTS=1 \
|
||||||
|
cargo run -p integration --bin integration -- test_video_recording
|
||||||
|
```
|
||||||
|
|
||||||
|
If you want full-test auto-recording from the environment as well, use:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
WARPUI_USE_REAL_DISPLAY_IN_INTEGRATION_TESTS=1 \
|
||||||
|
WARP_INTEGRATION_TEST_VIDEO=test_video_recording \
|
||||||
|
cargo run -p integration --bin integration -- test_video_recording
|
||||||
|
```
|
||||||
|
|
||||||
|
## Working pattern for agents
|
||||||
|
|
||||||
|
When asked to record or debug an integration test with video:
|
||||||
|
1. Identify the exact test name.
|
||||||
|
2. Decide whether recording should be explicit in the test or enabled via `WARP_INTEGRATION_TEST_VIDEO`.
|
||||||
|
3. Ensure the run uses `WARPUI_USE_REAL_DISPLAY_IN_INTEGRATION_TESTS=1`.
|
||||||
|
4. If the user wants visible interaction overlays, make sure the test dispatches mouse and keyboard events while recording is active.
|
||||||
|
5. After the run, inspect the timestamped artifact directory and report the output paths back to the user.
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
---
|
||||||
|
name: "Build image and start container for SSH testing"
|
||||||
|
command: |-
|
||||||
|
WARP_REPO_PATH=`git rev-parse --show-toplevel`
|
||||||
|
docker build -t {{image}} $WARP_REPO_PATH/app/tests/ssh
|
||||||
|
docker run -ditp 22:22 {{image}}
|
||||||
|
tags: ["docker"]
|
||||||
|
description: "Builds a Docker image and launches a container based on that image for SSH testing. You can run `ssh bash@0.0.0.0` or `ssh zsh@0.0.0.0` after running this. The password will be 'password'. After you first do this, you can manage the container with the Docker Desktop app. The container may stop (e.g. if you restart your computer), and you can usually bring it back by restarting it there (hit the play button)."
|
||||||
|
arguments:
|
||||||
|
- name: image
|
||||||
|
description: The name of the Docker image. If you have multiple on your machine in order to test different operating systems, it might be useful to denominate them based on that.
|
||||||
|
default_value: ssh_test_ubuntu_latest
|
||||||
|
author: Zheng Tao
|
||||||
|
shells: ["zsh", "bash"]
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
name: "Cherrypick commit into a latest release branch"
|
||||||
|
command: |-
|
||||||
|
git fetch;
|
||||||
|
BRANCH_NAME=$(git branch -r | grep -o '{{release}}_release/v0.20.*{{release}}' | sort | tail -n 1);
|
||||||
|
git checkout $BRANCH_NAME;
|
||||||
|
git pull;
|
||||||
|
COMMIT_HASH={{commit_hash}};
|
||||||
|
CP_BRANCH="cherrypick/${BRANCH_NAME}_${COMMIT_HASH:0:7}";
|
||||||
|
git checkout -b $CP_BRANCH;
|
||||||
|
git pull;
|
||||||
|
git cherry-pick {{commit_hash}} && git push --set-upstream origin $CP_BRANCH --no-verify;
|
||||||
|
echo -e "\n\nCreate PR using this URL: https://github.com/warpdotdev/warp-internal/compare/${BRANCH_NAME}...${CP_BRANCH}?quick_pull=1&template=cherrypick.md"
|
||||||
|
description: "Sets up a cherrypick of a commit (specified by hash) into the given release branch by creating a new local branch off of the release branch, performing the cherry-pick, and pushing the new branch to GitHub."
|
||||||
|
arguments:
|
||||||
|
- name: commit_hash
|
||||||
|
description: Commit hash for the commit to cherrypick
|
||||||
|
- name: release
|
||||||
|
description: "Name of the release channel to cherrypick into, possible values: [dev, preview, stable]"
|
||||||
|
author: Warp Team
|
||||||
|
shells: []
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
name: "Copy WarpDev Keychain to Warp local"
|
||||||
|
command: "security add-generic-password -a User -U -s warp -w \"$(security find-generic-password -a User -s dev.warp.Warp-Dev -w)\""
|
||||||
|
description: "Copies the refresh token from WarpDev into the local warp app so that it can authenticate."
|
||||||
|
author: Warp Team
|
||||||
|
shells: []
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
name: "Create GH pull request to release a feature."
|
||||||
|
command: open 'https://github.com/warpdotdev/warp-internal/compare/{{branch_name}}?expand=1&template=feature_flag.md'
|
||||||
|
description: "Creates a templated pull request that we use when releasing a feature (e.g. flipping a feature flag)."
|
||||||
|
arguments:
|
||||||
|
- name: branch_name
|
||||||
|
description: The name of the branch that enables the feature.
|
||||||
|
author: Warp Team
|
||||||
|
shells: []
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
---
|
||||||
|
name: "Run an integration test"
|
||||||
|
command: "WARPUI_USE_REAL_DISPLAY_IN_INTEGRATION_TESTS=1 cargo test --package integration --test integration -- {{test}}"
|
||||||
|
description: "Run a specific integration test."
|
||||||
|
arguments:
|
||||||
|
- name: test
|
||||||
|
description: The name of the integration test to run.
|
||||||
|
default_value: test_simple_example
|
||||||
|
author: Warp Team
|
||||||
|
shells: []
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
---
|
||||||
|
name: "Run unit test"
|
||||||
|
command: "cargo test --package warp --lib -- {{module_and_test}} --exact --nocapture"
|
||||||
|
description: "Run a specific unit test. This is much faster than running cargo test when this is all you need."
|
||||||
|
arguments:
|
||||||
|
- name: module_and_test
|
||||||
|
description: The specific `tests` module and the name of the test, separated by `::`
|
||||||
|
default_value: terminal::model::blocks::tests::test_separator
|
||||||
|
author: Zheng Tao
|
||||||
|
shells: []
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
---
|
||||||
|
name: "Run Warp locally with shell"
|
||||||
|
command: "WARP_SHELL_PATH={{shell}} cargo run"
|
||||||
|
description: "Runs warp with the particular shell so devs don't need to change their login shell to test a different shell locally"
|
||||||
|
arguments:
|
||||||
|
- name: shell
|
||||||
|
description: path of shell to use
|
||||||
|
author: Warp Team
|
||||||
|
shells: []
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
name: "Run Warp locally with version and channel"
|
||||||
|
command: "GIT_RELEASE_TAG={{version}} cargo run --bin {{channel}}"
|
||||||
|
description: "Runs Warp locally with a particular version and channel for development purposes"
|
||||||
|
arguments:
|
||||||
|
- name: version
|
||||||
|
description: version number to use, e.g. 1.2.3.4
|
||||||
|
- name: channel
|
||||||
|
description: channel type, e.g. dev or stable
|
||||||
|
default_value: dev
|
||||||
|
author: Warp Team
|
||||||
|
shells: []
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
---
|
||||||
|
name: "Bundle warp"
|
||||||
|
command: "./script/bundle -c {{channel}}"
|
||||||
|
description: ~
|
||||||
|
arguments:
|
||||||
|
- name: channel
|
||||||
|
description: "The channel that should be bundled. One of \"stable\", \"preview\", or \"dev\"."
|
||||||
|
author: Warp Team
|
||||||
|
shells: []
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
name: "Create new branch off of master"
|
||||||
|
command:
|
||||||
|
|
|
||||||
|
new_branch={{new_branch_name}};
|
||||||
|
git checkout master && git pull origin master && git rev-parse --verify "$new_branch";
|
||||||
|
if [[ $? -ne 0 ]]; then
|
||||||
|
git checkout -b "$new_branch";
|
||||||
|
else
|
||||||
|
yellow=`tput setaf 3`;
|
||||||
|
reset=`tput sgr0`;
|
||||||
|
echo "${yellow}Branch $new_branch already exists${reset}" && git checkout "$new_branch"
|
||||||
|
fi
|
||||||
|
description: "Starts a new clean branch off of master if it doesn't already exist"
|
||||||
|
arguments:
|
||||||
|
- name: new_branch_name
|
||||||
|
description: Name of the new branch for a task
|
||||||
|
author: Warp Team
|
||||||
|
shells: []
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
app/tests/ref
|
||||||
|
*.svg
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
# Contributor Covenant Code of Conduct
|
||||||
|
|
||||||
|
## Our Pledge
|
||||||
|
|
||||||
|
We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, caste, color, religion, or sexual identity and orientation.
|
||||||
|
|
||||||
|
We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community.
|
||||||
|
|
||||||
|
## Our Standards
|
||||||
|
|
||||||
|
Examples of behavior that contributes to a positive environment for our community include:
|
||||||
|
|
||||||
|
- Demonstrating empathy and kindness toward other people
|
||||||
|
- Being respectful of differing opinions, viewpoints, and experiences
|
||||||
|
- Giving and gracefully accepting constructive feedback
|
||||||
|
- Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience
|
||||||
|
- Focusing on what is best not just for us as individuals, but for the overall community
|
||||||
|
|
||||||
|
Examples of unacceptable behavior include:
|
||||||
|
|
||||||
|
- The use of sexualized language or imagery, and sexual attention or advances of any kind
|
||||||
|
- Trolling, insulting or derogatory comments, and personal or political attacks
|
||||||
|
- Public or private harassment
|
||||||
|
- Publishing others' private information, such as a physical or email address, without their explicit permission
|
||||||
|
- Other conduct which could reasonably be considered inappropriate in a professional setting
|
||||||
|
|
||||||
|
## Enforcement Responsibilities
|
||||||
|
|
||||||
|
Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful.
|
||||||
|
|
||||||
|
Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate.
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event.
|
||||||
|
|
||||||
|
## Enforcement
|
||||||
|
|
||||||
|
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement by emailing warp-coc at warp.dev. All complaints will be reviewed and investigated promptly and fairly.
|
||||||
|
|
||||||
|
All community leaders are obligated to respect the privacy and security of the reporter of any incident.
|
||||||
|
|
||||||
|
## Enforcement Guidelines
|
||||||
|
|
||||||
|
Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct:
|
||||||
|
|
||||||
|
### 1. Correction
|
||||||
|
|
||||||
|
**Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community.
|
||||||
|
|
||||||
|
**Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested.
|
||||||
|
|
||||||
|
### 2. Warning
|
||||||
|
|
||||||
|
**Community Impact**: A violation through a single incident or series of actions.
|
||||||
|
|
||||||
|
**Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban.
|
||||||
|
|
||||||
|
### 3. Temporary Ban
|
||||||
|
|
||||||
|
**Community Impact**: A serious violation of community standards, including sustained inappropriate behavior.
|
||||||
|
|
||||||
|
**Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban.
|
||||||
|
|
||||||
|
### 4. Permanent Ban
|
||||||
|
|
||||||
|
**Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals.
|
||||||
|
|
||||||
|
**Consequence**: A permanent ban from any sort of public interaction within the community.
|
||||||
|
|
||||||
|
## Attribution
|
||||||
|
|
||||||
|
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.1, available at [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
|
||||||
|
|
||||||
|
Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder][Mozilla CoC].
|
||||||
|
|
||||||
|
For answers to common questions about this code of conduct, see the FAQ at [https://www.contributor-covenant.org/faq][FAQ]. Translations are available at [https://www.contributor-covenant.org/translations][translations].
|
||||||
|
|
||||||
|
[homepage]: https://www.contributor-covenant.org
|
||||||
|
[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
|
||||||
|
[Mozilla CoC]: https://github.com/mozilla/diversity
|
||||||
|
[FAQ]: https://www.contributor-covenant.org/faq
|
||||||
|
[translations]: https://www.contributor-covenant.org/translations
|
||||||
+168
@@ -0,0 +1,168 @@
|
|||||||
|
# Contributing to Warp
|
||||||
|
|
||||||
|
Thanks for helping improve Warp! This guide explains how to open issues, propose changes, and get your work reviewed.
|
||||||
|
|
||||||
|
## TL;DR
|
||||||
|
|
||||||
|
- Bug fixes are welcome for any issue. All bugs are marked as `ready-to-implement`.
|
||||||
|
- Feature requests must be marked `ready-to-spec` or `ready-to-implement` before PRs are accepted.
|
||||||
|
- Specs are the place where technical and design discussion on larger issues happen.
|
||||||
|
- Oz automatically triages incoming issues and reviews open PRs.
|
||||||
|
|
||||||
|
## How Contributing to Warp Works
|
||||||
|
|
||||||
|
Warp's contribution model is shaped by [Oz](https://oz.warp.dev), an agent that automates parts of triage, spec writing, implementation, and review. Compared with a typical open-source repository, a few things work differently here:
|
||||||
|
|
||||||
|
- **Issues are the starting point for everything.** Discussion, scoping, and design happen on the issue before any PR is opened.
|
||||||
|
- **Feature requests differ from bug fixes:**
|
||||||
|
- Features are gated by readiness labels — `ready-to-spec`, then `ready-to-implement` once the design is settled — that signal when contributors can pick up the work. Discussion alone is not approval to begin work.
|
||||||
|
- Feature work needs a written spec first: feature requests go through a spec PR (a *product spec* + *tech spec* committed under [`specs/`](specs/)) before any code is written.
|
||||||
|
- Bug fixes skip both steps; they are implicitly `ready-to-implement` once triaged.
|
||||||
|
- **Review is largely automated.** When you open a PR, Oz is auto-assigned and produces an initial review. Once Oz approves, it automatically requests a follow-up review from a Warp team subject-matter expert — you do not need to assign human reviewers yourself.
|
||||||
|
|
||||||
|
### Readiness labels
|
||||||
|
|
||||||
|
The Warp team applies one of the following labels when an issue is ready for contribution:
|
||||||
|
|
||||||
|
- **`ready-to-spec`** — The problem is understood but the design is open. Open a spec PR with a *product spec* (`product.md`) and a *tech spec* (`tech.md`) under [`specs/`](specs/) — see [Opening a Spec PR](#opening-a-spec-pr) for what goes in each. This label is **reserved for feature requests**.
|
||||||
|
- **`ready-to-implement`** — The design is settled. Open a code PR. **All triaged bug reports are implicitly `ready-to-implement`** once accepted — you don't need to wait for an explicit label on a confirmed bug.
|
||||||
|
- **`needs-mocks`** — Design mocks are required before implementation can begin. Wait for the Warp team to land them.
|
||||||
|
|
||||||
|
Anyone can pick up a ready issue — readiness labels are not assignments, and the best implementation wins through normal review. If an issue has been sitting un-triaged or you'd like readiness re-evaluated, mention **@oss-maintainers** in a comment to flag it for the team.
|
||||||
|
|
||||||
|
## Contribution Flow
|
||||||
|
|
||||||
|
Steps owned by you (the contributor) are shown in yellow; steps owned by the Warp team or Oz are shown in blue.
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TD
|
||||||
|
A[File an issue] --> B{Warp team triages}
|
||||||
|
B -- ready-to-spec<br/>(feature requests) --> C[Open spec PR<br/>product.md + tech.md]
|
||||||
|
B -- needs-mocks --> D[Design mocks produced]
|
||||||
|
D --> E[Open code PR]
|
||||||
|
C -- specs approved --> E
|
||||||
|
B -- ready-to-implement<br/>(incl. all triaged bugs) --> E
|
||||||
|
E --> F[Oz review → SME review → CI → merge]
|
||||||
|
|
||||||
|
classDef contributor fill:#fef3c7,stroke:#b45309,color:#78350f;
|
||||||
|
classDef warpTeam fill:#dbeafe,stroke:#1d4ed8,color:#1e3a8a;
|
||||||
|
class A,C,E contributor;
|
||||||
|
class B,D,F warpTeam;
|
||||||
|
```
|
||||||
|
|
||||||
|
## Filing a Good Issue
|
||||||
|
|
||||||
|
Search [existing issues](https://github.com/warpdotdev/warp/issues) before filing to avoid duplicates. Use the issue templates when filing.
|
||||||
|
|
||||||
|
If you're already running Warp, the fastest way to file is the `/feedback` command — it opens a public GitHub issue with relevant context (logs, environment details) automatically attached.
|
||||||
|
|
||||||
|
### Bug reports
|
||||||
|
|
||||||
|
A good bug report includes:
|
||||||
|
|
||||||
|
- A clear title and a one-paragraph summary of the problem.
|
||||||
|
- Steps to reproduce (with a minimal example where possible).
|
||||||
|
- Expected vs. actual behavior.
|
||||||
|
- Warp version and OS (see `Settings → About` or `warp --version`).
|
||||||
|
- Logs, screenshots, or screen recordings when relevant.
|
||||||
|
|
||||||
|
Once an issue is triaged as a bug (by Oz's triage agent or a maintainer), it is implicitly **`ready-to-implement`** — you can pick it up and open a code PR without waiting for a separate label.
|
||||||
|
|
||||||
|
### Feature requests
|
||||||
|
|
||||||
|
A good feature request describes the user-facing problem before any proposed implementation. Include:
|
||||||
|
|
||||||
|
- The user need or pain point, and who experiences it.
|
||||||
|
- The current behavior and why it falls short.
|
||||||
|
- A sketch of the desired behavior or workflow (a short example or mock is helpful but not required).
|
||||||
|
- Any relevant constraints (compatibility, related features, prior art, etc.).
|
||||||
|
|
||||||
|
Feature requests are the path that goes through the spec flow: a maintainer applies **`ready-to-spec`** when the problem is understood and the design is open for contributors. From there, the next step is a spec PR — not a code PR.
|
||||||
|
|
||||||
|
Automated triage may add informational labels (`area:*`, `repro:*`, etc.). Those do not affect readiness.
|
||||||
|
|
||||||
|
## Opening a Spec PR
|
||||||
|
|
||||||
|
Issues labeled `ready-to-spec` need a spec before code can begin. A spec consists of two short documents committed under [`specs/GH<issue-number>/`](specs/):
|
||||||
|
|
||||||
|
- **`product.md`** (the *product spec*) — Defines the desired behavior from the consumer's perspective (the user, an API caller, a CLI user, etc.) and stays out of implementation detail. The core is a numbered list of **testable behavior invariants** covering the happy path, user-visible states, inputs and responses, and edge cases (empty / error / loading, cancellation, offline, permission denied, races, accessibility). Optional sections: problem statement, goals / non-goals, Figma link, open questions.
|
||||||
|
- **`tech.md`** (the *tech spec*) — The implementation plan, grounded in this codebase. Required sections: **Context** (the current system and relevant files with line references), **Proposed changes** (modules touched, new types / APIs / state, data flow, tradeoffs), and **Testing and validation** (how each invariant from the product spec will be verified). Optional: end-to-end flow, Mermaid diagrams, risks, parallelization, follow-ups.
|
||||||
|
|
||||||
|
To open a spec PR:
|
||||||
|
|
||||||
|
1. Add `specs/GH<issue-number>/product.md` and `specs/GH<issue-number>/tech.md`. See [`specs/GH408/`](specs/GH408/), [`specs/GH1063/`](specs/GH1063/), and [`specs/GH1066/`](specs/GH1066/) for examples of well-structured specs, and browse the rest of [`specs/`](specs/) for more. The [`/write-product-spec`](.agents/skills/write-product-spec/SKILL.md) and [`/write-tech-spec`](.agents/skills/write-tech-spec/SKILL.md) skills are available to scaffold these for you.
|
||||||
|
2. Use the PR as the home for product and technical discussion.
|
||||||
|
3. Once the specs are approved, implementation generally continues on the same PR. In rarer cases — for example, if a large spec is merged on its own so the implementation can be broken up — it can move to a linked follow-up PR.
|
||||||
|
|
||||||
|
## Opening a Code PR
|
||||||
|
|
||||||
|
For issues labeled `ready-to-implement` (this includes any triaged bug):
|
||||||
|
|
||||||
|
1. Branch from `master`.
|
||||||
|
2. Implement the change and add tests (see [Testing](#testing)).
|
||||||
|
3. Run `./script/presubmit` and fix any failures before pushing.
|
||||||
|
4. Open a PR using the [pull request template](.github/pull_request_template.md) and add a changelog entry (`CHANGELOG-NEW-FEATURE`, `CHANGELOG-IMPROVEMENT`, or `CHANGELOG-BUG-FIX`); omit only for docs-only or refactoring-only changes.
|
||||||
|
5. Keep the PR focused on a single logical change and merge `master` in before the PR enters review.
|
||||||
|
|
||||||
|
You **do not need to manually request reviewers**. Oz is auto-assigned to PRs that target a ready issue and produces an initial review. After Oz approves, it automatically requests a follow-up review from the appropriate Warp team subject-matter expert.
|
||||||
|
|
||||||
|
After you push changes that address Oz's feedback, comment `/oz-review` on the PR to request a re-review — you can do this up to **three times** per PR. If something looks stuck or you need more reviews than that, mention **@oss-maintainers** on the PR to escalate to the team.
|
||||||
|
|
||||||
|
## Using a Coding Agent
|
||||||
|
|
||||||
|
You can use **any coding agent** to implement a contribution — for example, Warp's built-in agent, Claude Code, Codex, Gemini CLI, or others — or no agent at all. This repository ships agent-readable context (skills under [`.agents/skills/`](.agents/skills/), specs under [`specs/`](specs/), and [`WARP.md`](WARP.md)) that any harness supporting these formats can pick up.
|
||||||
|
|
||||||
|
If you'd rather have an **Oz cloud agent** implement a ready issue for you, mention **@oss-maintainers** on the issue to request it. Approved requests run **for free** on complimentary Oz credits — you don't need to set up your own Oz account or pay for compute.
|
||||||
|
|
||||||
|
## Becoming a Collaborator
|
||||||
|
|
||||||
|
Contributors with several merged PRs may be invited to become collaborators. Collaborators receive expanded permissions including the ability to:
|
||||||
|
|
||||||
|
- Assign [Oz](https://warp.dev/oz) to work on issues by mentioning `@oz` in a comment on any issue that has a readiness label.
|
||||||
|
- Use complimentary Oz credits for contributions to this repository.
|
||||||
|
- Apply and manage issue labels.
|
||||||
|
|
||||||
|
## Development Setup
|
||||||
|
|
||||||
|
See [README.md](README.md) and [WARP.md](WARP.md) for the full engineering guide. Quick start:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./script/bootstrap # platform-specific setup
|
||||||
|
cargo run # build and run Warp
|
||||||
|
./script/presubmit # fmt, clippy, and tests
|
||||||
|
```
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
Tests are required for most code changes:
|
||||||
|
|
||||||
|
- **Bug fixes** should include a regression test that would have caught the bug.
|
||||||
|
- **Algorithmic or non-trivial logic** needs unit tests.
|
||||||
|
- **User-facing flows** should have end-to-end coverage under [`crates/integration/`](crates/integration/) whenever the behavior can be exercised that way. The bar is high-quality coverage of the changes you ship — with agent-driven development the expectation is more integration tests, not just coverage of P0 paths. If a flow is worth shipping, it's usually worth an integration test.
|
||||||
|
|
||||||
|
Run unit tests with `cargo nextest run`. See [WARP.md](WARP.md) for more detail.
|
||||||
|
|
||||||
|
## Code Style
|
||||||
|
|
||||||
|
- `cargo fmt` and `cargo clippy --workspace --all-targets --all-features --tests -- -D warnings` must pass.
|
||||||
|
- Prefer imports over path qualifiers, inline format args (`println!("{x}")`), and exhaustive `match` over `_` wildcards.
|
||||||
|
- See [WARP.md](WARP.md) for the full style guide, including WarpUI patterns and terminal model locking rules.
|
||||||
|
|
||||||
|
## Commit and Branch Conventions
|
||||||
|
|
||||||
|
- Branch names should be prefixed with your handle (e.g. `alice/fix-parser`).
|
||||||
|
- Commit messages should explain *what* and *why*, not just *what*.
|
||||||
|
|
||||||
|
## Code of Conduct
|
||||||
|
|
||||||
|
This project adopts the [Contributor Covenant](https://www.contributor-covenant.org/) (v2.1) as its code of conduct. All contributors and maintainers are expected to follow it in every project space. See [`CODE_OF_CONDUCT.md`](CODE_OF_CONDUCT.md) for the full text, or report violations to warp-coc at warp.dev.
|
||||||
|
|
||||||
|
## Reporting Security Issues
|
||||||
|
|
||||||
|
See [`SECURITY.md`](SECURITY.md) for our security disclosure policy and private reporting channels. **Do not open public issues for security vulnerabilities.**
|
||||||
|
|
||||||
|
## Getting Help
|
||||||
|
|
||||||
|
- Browse the [Warp docs](https://docs.warp.dev/).
|
||||||
|
- Join the [Slack Community](https://go.warp.dev/join-preview) to ask questions and connect with other contributors.
|
||||||
|
- Open a [GitHub issue](https://github.com/warpdotdev/warp/issues) for bugs or feature requests.
|
||||||
Generated
+16782
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user