first pass of merging in warp (doesn't build)

This commit is contained in:
Ryan Ward
2026-07-01 16:08:58 -05:00
parent 2f64909469
commit 4770ac06b5
3662 changed files with 414574 additions and 89772 deletions
+1 -2
View File
@@ -1,6 +1,6 @@
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"]
labels: ["bug"]
body:
- type: checkboxes
attributes:
@@ -134,4 +134,3 @@ body:
- Ignore
validations:
required: false
@@ -6,7 +6,7 @@ body:
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"
- label: "I have [searched Warp feature requests](https://github.com/warpdotdev/warp/issues?q=is%3Aissue+label%3Aenhancement) 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
@@ -42,6 +42,7 @@ body:
- macOS
- Linux
- Windows
- Cross-platform
validations:
required: true
- type: dropdown
+80 -17
View File
@@ -7,7 +7,7 @@
# 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
/ @warpdotdev/oss-maintainers
###########################################################################
# Team: App
@@ -33,7 +33,8 @@
/crates/ai/src/index/ @kevinyang372
/crates/ai/src/project_context/ @kevinyang372
/crates/editor/ @kevinyang372 @bnavetta
/crates/lsp/ @kevinyang372
/crates/languages/ @kevinyang372 @bnavetta
/crates/lsp/ @kevinyang372 @moirahuang
/crates/repo_metadata/ @kevinyang372
# Settings and keybindings
@@ -45,7 +46,7 @@
/app/src/terminal/input/ @vkodithala
/app/src/pane_group/ @vkodithala
/app/src/tab.rs @vkodithala
/crates/warp_ripgrep/ @vkodithala @moirahuang @szgupta
/crates/warp_ripgrep/ @moirahuang @szgupta
# Command palette
/app/src/command_palette.rs @acarl005
@@ -53,7 +54,6 @@
/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
@@ -62,6 +62,11 @@
/app/src/code/file_tree/ @moirahuang
/app/src/workspace/view/global_search/ @moirahuang
# Tab configs and worktrees
/app/src/tab_configs/ @moirahuang
/app/resources/tab_configs/ @moirahuang
/crates/warp_util/src/worktree_names.rs @moirahuang
# Onboarding / code review and git diff
/app/src/ai/onboarding.rs @kevinchevalier
/app/src/code_review/ @kevinchevalier
@@ -70,9 +75,18 @@
/crates/onboarding/ @kevinchevalier
# MCP and skills
/app/src/ai/mcp/ @peicodes
/app/src/ai/skills/ @peicodes
/crates/mcp/ @peicodes
/app/src/ai/mcp/ @peicodes @vkodithala
/app/src/ai/skills/ @peicodes @vkodithala
/crates/mcp/ @peicodes @vkodithala
/resources/bundled/skills/feedback/ @captainsafia
# File-based MCP (overrides MCP and skills above)
/app/src/ai/mcp/file_based_manager.rs @vkodithala
/app/src/ai/mcp/file_based_manager_tests.rs @vkodithala
/app/src/ai/mcp/file_mcp_watcher.rs @vkodithala
/app/src/ai/mcp/file_mcp_watcher_tests.rs @vkodithala
/app/src/ai/mcp/dummy_file_based_manager.rs @vkodithala
/app/src/ai/mcp/dummy_file_mcp_watcher.rs @vkodithala
# Conversation management / credit usage footer / input UI / natural-language detection
/app/src/ai/active_agent_views_model.rs @harryalbert
@@ -86,18 +100,27 @@
/crates/input_classifier/ @harryalbert
/crates/natural_language_detection/ @harryalbert
# Notifications
/app/src/notification.rs @harryalbert
/app/src/ai/agent_management/notifications/ @harryalbert
/app/src/terminal/view/inline_banner/ @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/ai/blocklist/ @zachbai @MaggieShan
/app/src/root_view.rs @zachbai @MaggieShan
/app/src/terminal/bootstrap.rs @zachbai
/app/src/terminal/warpify/ @zachbai
/app/src/settings_view/warpify_page.rs @zachbai
/app/src/terminal/warpify/ @zachbai @MaggieShan
/app/src/settings_view/warpify_page.rs @zachbai @MaggieShan
/app/assets/bundled/bootstrap/ @zachbai
/crates/warp_completer/ @zachbai @szgupta @alokedesai
/crates/warp_completer/ @zachbai @szgupta @alokedesai @acarl005
# Agent mode
/app/src/ai/agent/ @zachbai
# Vertical tabs
/app/src/workspace/view/vertical_tabs.rs @johnturcoo
/app/src/workspace/view/vertical_tabs/ @johnturcoo
# Image attachment, voice input, and passive suggestions
/app/src/ai/attachment_utils.rs @Advait-M
/app/src/ai/voice/ @Advait-M
@@ -106,26 +129,58 @@
/app/src/voice/ @Advait-M
/crates/voice_input/ @Advait-M
# Rich input
/app/src/terminal/view/use_agent_footer/ @Advait-M
/app/src/terminal/view/use_agent_footer/warpify_footer.rs @MaggieShan
/app/src/terminal/input/cli_agent.rs @Advait-M
# UI framework
/crates/warpui/ @vorporeal
/crates/warpui_core/ @vorporeal
/crates/warpui/ @vorporeal @alokedesai @acarl005
/crates/warpui_core/ @vorporeal @alokedesai
/crates/warpui_extras/ @vorporeal
/crates/ui_components/ @vorporeal
/crates/warpui_extras/src/user_preferences @danielpeng
/crates/ui_components/ @vorporeal @acarl005 @zachbai @bnavetta
# 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/cli_agent.rs @zachbai @harryalbert
/app/src/terminal/cli_agent_tests.rs @zachbai @harryalbert
/app/src/terminal/cli_agent_sessions/ @zachbai @harryalbert
/app/src/terminal/input/rewind/ @alokedesai
/app/src/workspace/rewind_confirmation_dialog.rs @alokedesai
/app/src/platform/mac/ @alokedesai
/resources/linux/ @acarl005
# Windows and Linux (and winit) platform
/crates/warpui/src/windowing/winit/ @acarl005 @vorporeal @alokedesai
/app/src/terminal/local_tty/windows/ @abhishekp106 @vorporeal @acarl005
/app/src/autoupdate/windows.rs @acarl005
/app/src/autoupdate/linux.rs @vorporeal
/app/src/app_services/windows/ @acarl005
/app/src/app_services/linux/ @vorporeal
/app/src/terminal/writeable_pty/bootstrap_file/windows.rs @acarl005
/app/assets/bundled/bootstrap/pwsh.ps1 @acarl005
/app/assets/bundled/bootstrap/pwsh_init_shell.ps1 @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
# Long-running shell commands
/app/src/ai/blocklist/action_model/execute/shell_command.rs @vkodithala @MaggieShan
/app/src/ai/blocklist/block/cli.rs @vkodithala @MaggieShan
/app/src/ai/blocklist/block/cli_controller.rs @vkodithala @MaggieShan
# Vim
/crates/vim/ @liliwilson
/app/src/vim_registers.rs @liliwilson
/app/src/code/editor/view/vim_handler.rs @liliwilson
/app/src/code/editor/view/vim_handler_tests.rs @liliwilson
/app/src/editor/view/vim_handler_test.rs @liliwilson
/app/src/terminal/view/inline_banner/vim_mode.rs @liliwilson
/app/src/settings/vim_banner.rs @liliwilson
###########################################################################
# Team: Platform
###########################################################################
@@ -178,5 +233,13 @@
/crates/managed_secrets/ @bnavetta @ianhodge
/crates/warp_web_event_bus/ @bnavetta @ianhodge
# Command signatures v2
/command-signatures-v2/ @zachbai
# OS-level bootstrap scripts
/script/linux/bootstrap @acarl005
/script/macos/bootstrap @acarl005
/script/windows/bootstrap.ps1 @acarl005
# Shell completions in the Warp CLI
/crates/warp_cli/src/completions.rs @zachbai @szgupta @alokedesai
@@ -37,5 +37,6 @@ runs:
-e GIT_RELEASE_TAG="$GIT_RELEASE_TAG" \
-e GITHUB_ACTIONS="$GITHUB_ACTIONS" \
-e GITHUB_OUTPUT="/dev/null" \
${SETTINGS_SCHEMA_CACHE:+-e SETTINGS_SCHEMA_CACHE=/github/workspace/.settings_schema_cache.json} \
arch-bundle-builder \
${{ inputs.channel }} ${{ inputs.release-tag }} ${{ inputs.arch }} ${{ inputs.artifact }}
+22 -5
View File
@@ -53,6 +53,22 @@ runs:
echo "is-workspace-root=false" >> $GITHUB_OUTPUT
fi
# Materialize Git LFS objects
- name: Fetch Git LFS objects
shell: bash
run: |
cd "${{ steps.repo-root.outputs.path }}"
git lfs install --local
git lfs pull
# Print a per-file listing of the LFS payloads in debug mode
- name: Verify LFS payloads
if: ${{ runner.debug == '1' }}
shell: bash
run: |
cd "${{ steps.repo-root.outputs.path }}"
git lfs ls-files --size
- uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
if: ${{ inputs.is_self_hosted != 'true' && !startsWith(runner.name, 'nsc-runner') }}
with:
@@ -85,6 +101,11 @@ runs:
with:
ssh-private-key: ${{ inputs.ssh_key }}
- uses: maxim-lobanov/setup-xcode@ed7a3b1fda3918c0306d1b724322adc0b8cc0a90 # v1.7.0
if: ${{ inputs.target_os == 'macos' && inputs.is_self_hosted != 'true' }}
with:
xcode-version: '26'
- name: Install dependencies
shell: bash
run: |
@@ -127,7 +148,7 @@ runs:
fi
- name: Install Node
uses: actions/setup-node@3235b876344d2a9aa001b8d1453c930bba69e610 # v3.9.1
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: 20.9.0
@@ -159,7 +180,3 @@ runs:
echo "::error::protoc install step failed on ${{ inputs.target_os }}"
exit 1
- uses: maxim-lobanov/setup-xcode@ed7a3b1fda3918c0306d1b724322adc0b8cc0a90 # v1.7.0
if: ${{ inputs.target_os == 'macos' && inputs.is_self_hosted != 'true' }}
with:
xcode-version: '26'
+2 -2
View File
@@ -7,7 +7,7 @@ updates:
directory: "/"
schedule:
interval: "daily"
reviewers:
assignees:
- "warpdotdev/tech-leads"
registries:
- github-private
@@ -17,7 +17,7 @@ updates:
directory: "/"
schedule:
interval: "daily"
reviewers:
assignees:
- "warpdotdev/tech-leads"
cooldown:
# Don't update to any action release that is less than two weeks old.
+25 -16
View File
@@ -1,34 +1,41 @@
## Description
<!-- Please remember to add your design buddy onto the PR for review, if it contains any UI changes! -->
## Linked Issue
<!--
Link the GitHub issue this PR addresses. Before opening this PR, please confirm:
-->
- [ ] The linked issue is labeled `ready-to-spec` or `ready-to-implement`.
- [ ] Where appropriate, screenshots or a short video of the implementation are included below (especially for user-visible or 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?
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
Manual testing is required for changes that can be manually tested, and almost all changes can be manually tested. If your change can be manually tested, please include screenshots or a screen recording that show it working end to end.
You can run the app locally using `./script/run` - see AGENTS.md for more details on how to get set up.
-->
## 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.
- [ ] I have manually tested my changes locally with `./script/run`
### Screenshots / Videos
<!-- Attach screenshots or a short video demonstrating the change, where appropriate. Remove this section if it is not relevant to your PR. -->
## Agent Mode
- [ ] Warp Agent Mode - This PR was created via Warp's AI Agent Mode
## Changelog Entries for Stable
<!--
## 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.
-->
- 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.
- NONE: Explicitly opt out of changelog inclusion. Use `CHANGELOG-NONE` for PRs that should never appear in the changelog (e.g. refactors, internal tooling, CI changes). This prevents the changelog agent from inferring an entry.
CHANGELOG-NEW-FEATURE: {{text goes here...}}
CHANGELOG-IMPROVEMENT: {{text goes here...}}
@@ -36,3 +43,5 @@ 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...}}
CHANGELOG-NONE
-->
@@ -0,0 +1,171 @@
// Follows up on stale external-contributor PRs with active requested-changes
// reviews: posts escalating reminders, then closes after the final warning.
// Invoked from .github/workflows/stale_requested_changes_prs.yml via
// actions/github-script; `github` and `context` are injected by that action.
module.exports = async ({ github, context }) => {
const { owner, repo } = context.repo;
// Default to "full" if unset, because scheduled (cron) runs don't provide inputs
const mode = (context.payload.inputs && context.payload.inputs.mode) || 'full';
const canRemind = mode === 'reminder-only' || mode === 'full';
const canClose = mode === 'full';
const DAY_MS = 24 * 60 * 60 * 1000;
const REMINDER_DAYS = [7, 10];
const FINAL_WARNING_DAY = 10;
const CLOSE_DAY = 14;
const EXTERNAL_LABEL = 'external-contributor';
const EXEMPT_LABEL = 'no-autoclose';
const BOT_LOGIN = 'github-actions[bot]';
const markerFor = (stage) => `<!-- stale-requested-changes:stage=${stage} -->`;
const now = Date.now();
const ts = (value) => (value ? new Date(value).getTime() : 0);
// Returns the submitted_at (ms) of the most recent active requested-changes
// review, or 0 when no reviewer currently has changes requested. A reviewer's
// latest *decisive* review (APPROVED / CHANGES_REQUESTED / DISMISSED, tracked
// separately from COMMENTED so a later comment-reply doesn't flip state)
// determines their effective state.
const latestActiveChangesRequestedAt = (reviews) => {
const decisive = new Map();
const latest = new Map();
for (const r of reviews) {
const login = r.user && r.user.login;
if (!login) continue;
const at = ts(r.submitted_at);
if (!latest.has(login) || at > latest.get(login).at) {
latest.set(login, { state: r.state, at });
}
if (['APPROVED', 'CHANGES_REQUESTED', 'DISMISSED'].includes(r.state)) {
if (!decisive.has(login) || at > decisive.get(login).at) {
decisive.set(login, { state: r.state, at });
}
}
}
let latestAt = 0;
for (const login of latest.keys()) {
const eff = decisive.get(login) || latest.get(login);
if (eff.state === 'CHANGES_REQUESTED') latestAt = Math.max(latestAt, eff.at);
}
return latestAt;
};
const openPRs = await github.paginate(github.rest.pulls.list, {
owner,
repo,
state: 'open',
per_page: 100,
});
const summary = [];
for (const pr of openPRs) {
const labels = (pr.labels || []).map((l) => l.name);
const author = pr.user && pr.user.login;
if (pr.draft) continue;
if (pr.user && pr.user.type === 'Bot') continue;
// Skip internal member PRs and PRs with the no-autoclose label
if (!labels.includes(EXTERNAL_LABEL)) continue;
if (labels.includes(EXEMPT_LABEL)) continue;
const reviews = await github.paginate(github.rest.pulls.listReviews, {
owner,
repo,
pull_number: pr.number,
per_page: 100,
});
const changesRequestedAt = latestActiveChangesRequestedAt(reviews);
if (!changesRequestedAt) continue;
const [issueComments, reviewComments] = await Promise.all([
github.paginate(github.rest.issues.listComments, { owner, repo, issue_number: pr.number, per_page: 100 }),
github.paginate(github.rest.pulls.listReviewComments, { owner, repo, pull_number: pr.number, per_page: 100 }),
]);
// Server-recorded head-branch push time. commit.pushedDate and the
// force-push event timestamp are GitHub-controlled, so a backdated or
// force-pushed commit still resets the timer (unlike commit author/committer
// dates, which the contributor controls).
const pushData = await github.graphql(
`query($owner: String!, $repo: String!, $number: Int!) {
repository(owner: $owner, name: $repo) {
pullRequest(number: $number) {
commits(last: 1) { nodes { commit { pushedDate committedDate } } }
timelineItems(last: 50, itemTypes: [HEAD_REF_FORCE_PUSHED_EVENT]) {
nodes { ... on HeadRefForcePushedEvent { createdAt } }
}
}
}
}`,
{ owner, repo, number: pr.number }
);
const prGraph = pushData.repository.pullRequest;
const headCommit = (prGraph.commits.nodes[0] || {}).commit || {};
// Staleness anchor: the most recent active requested-changes review, plus
// author-driven events that reset the timer (PR creation, head-branch push,
// and comments by the PR author). Anchoring on the latest review ensures a
// subsequent requested-changes review restarts the window instead of
// measuring from stale, pre-review author activity. Maintainer/third-party/
// bot activity is otherwise ignored.
let lastActivity = Math.max(ts(pr.created_at), changesRequestedAt);
lastActivity = Math.max(lastActivity, ts(headCommit.pushedDate || headCommit.committedDate));
for (const ev of prGraph.timelineItems.nodes) {
lastActivity = Math.max(lastActivity, ts(ev.createdAt));
}
for (const c of issueComments) {
if (c.user && c.user.login === author) lastActivity = Math.max(lastActivity, ts(c.created_at));
}
for (const c of reviewComments) {
if (c.user && c.user.login === author) lastActivity = Math.max(lastActivity, ts(c.created_at));
}
const inactiveDays = (now - lastActivity) / DAY_MS;
// A stage counts as sent only when its marker comment was posted by our own
// workflow identity in the current window (at/after the last activity).
const sentStages = new Set();
for (const c of issueComments) {
if (!c.user || c.user.login !== BOT_LOGIN) continue;
if (ts(c.created_at) < lastActivity) continue;
for (const stage of REMINDER_DAYS) {
if ((c.body || '').includes(markerFor(stage))) sentStages.add(stage);
}
}
const dueStage = [...REMINDER_DAYS].reverse().find((s) => inactiveDays >= s);
const finalWarningSent = sentStages.has(FINAL_WARNING_DAY);
const shouldClose = inactiveDays >= CLOSE_DAY && finalWarningSent;
let action = 'none';
if (shouldClose) action = 'close';
else if (dueStage !== undefined && !sentStages.has(dueStage)) action = `remind:${dueStage}`;
const days = Math.floor(inactiveDays);
console.log(`PR #${pr.number} (@${author}, inactive ${days}d): ${action}${action === 'none' ? '' : ` [mode=${mode}]`}`);
if (action === 'none') continue;
summary.push(`#${pr.number} ${action} (${days}d)`);
if (action === 'close') {
if (!canClose) continue;
await github.rest.issues.createComment({
owner,
repo,
issue_number: pr.number,
body: `Closing this pull request because the requested changes have gone unaddressed for over ${CLOSE_DAY} days. If you'd like to continue, push your updates and reopen the PR (or comment to ask a maintainer to reopen) — we'd be glad to pick it back up.`,
});
await github.rest.pulls.update({ owner, repo, pull_number: pr.number, state: 'closed' });
continue;
}
if (!canRemind) continue;
const stage = Number(action.split(':')[1]);
const remaining = Math.max(1, CLOSE_DAY - days);
const body = stage === FINAL_WARNING_DAY
? `Hi @${author} — final reminder: a reviewer requested changes on this PR and it has been inactive for ${days} days. It will be **automatically closed in about ${remaining} day(s)** unless you push updates or reply. Maintainers can apply the \`${EXEMPT_LABEL}\` label to keep it open.\n\n${markerFor(stage)}`
: `Hi @${author} — a reviewer requested changes on this PR and it hasn't had activity from you in ${days} days. When you get a chance, please push updates or reply to the review so a reviewer can take another look. Without activity, this PR will be automatically closed after ${CLOSE_DAY} days of inactivity.\n\n${markerFor(stage)}`;
await github.rest.issues.createComment({ owner, repo, issue_number: pr.number, body });
}
console.log(`mode=${mode}; acted on ${summary.length} PR(s): ${summary.join(', ') || 'none'}`);
};
+72
View File
@@ -0,0 +1,72 @@
name: Changelog Draft
on:
# workflow_dispatch is restricted to users with write access to the repo.
# External contributors (fork-based) cannot trigger this workflow.
workflow_dispatch:
inputs:
channel:
description: "Release channel (stable, preview, dev)"
required: true
type: choice
options:
- stable
- preview
- dev
release_tag:
description: "Release tag (e.g. v0.2026.05.06.09.12.stable_00)"
required: true
type: string
attribution:
description: "Attribution mode"
required: false
type: choice
options:
- external-only
- all
- none
default: external-only
permissions:
contents: read
pull-requests: read
jobs:
draft:
name: Generate changelog draft
runs-on: namespace-profile-ubuntu-small
steps:
- name: Check out code
uses: namespacelabs/nscloud-checkout-action@938f5d2d403d6224d9a0c0dc559b1dae09c2ede4 # v8.1.1
with:
# Check out the default branch (not the release tag) so the skill
# files and scripts are always available — older release tags may
# not contain them. The release_tag is used only as the git range
# endpoint by the skill.
fetch-depth: 0
- name: Generate changelog draft
uses: warpdotdev/oz-agent-action@ce1621abf6a8ed8afdd4e4cc994545ede8fe1c6f # main
with:
prompt: |
Generate a changelog draft for the ${{ inputs.channel }} channel, release tag ${{ inputs.release_tag }}.
Attribution mode: ${{ inputs.attribution }}
Output directory: ${{ runner.temp }}/changelog-draft
Follow the workflow in .agents/skills/changelog-draft/SKILL.md exactly.
When fetching PR data, pass the checked-out repository ("${{ github.repository }}") to fetch_prs.py and rely on the script's repo-sync normalization to resolve public warpdotdev/warp PR numbers, URLs, and authors. The script intentionally omits non-repo-sync PRs from warp-internal because they are private internal changes. Do not infer or synthesize public PR links manually.
After writing the output files, print the full contents of changelog-draft.md to stdout so it appears in the workflow log.
You are running in a GitHub Actions workflow. The repo is checked out at the default branch (HEAD). Use the release_tag input as the git range endpoint — do NOT check out the release tag. `gh` is authenticated. Do not commit, push, or create PRs.
warp_api_key: ${{ secrets.WARP_API_KEY }}
share: team
- name: Upload changelog artifacts
uses: namespace-actions/upload-artifact@f6ccaacc655aec41b93af180d1d7eef21af862d2 # v1.0.3
with:
name: changelog-draft
path: |
${{ runner.temp }}/changelog-draft/changelog-draft.md
${{ runner.temp }}/changelog-draft/changelog-draft.json
+1 -1
View File
@@ -14,7 +14,7 @@ jobs:
if: github.event.pull_request.draft == false
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
fetch-depth: 0
# We don't actually need the contents of the files, just their names.
+134 -22
View File
@@ -57,7 +57,7 @@ jobs:
wasm-runner: ${{ steps.wasm_runner_type.outputs.value }}
steps:
- name: Checkout sources
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
- name: Check changed files
uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1
@@ -93,11 +93,13 @@ jobs:
# Federation provider is configured to only trust the base repository.
# We skip the auth + gcloud install steps in those runs and exclude SSH
# integration tests (which require gcloud to tunnel into a GCP test VM)
# via the filter suffix below. Tests that need gcloud all have `_ssh_`
# in their name. Fork PRs lose SSH integration test coverage; those
# tests still run post-merge against `master`.
# via the filter suffix below. Tests that need gcloud either have
# `_ssh_` in their name or exercise the remote-server SSH path. Fork PRs
# lose SSH integration test coverage; those tests still run post-merge
# against `master`.
HAS_GCP_AUTH: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }}
EXCLUDE_SSH_TESTS_FILTER: ${{ (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository) && ' and not test(/_ssh_/)' || '' }}
EXCLUDE_SSH_TESTS_FILTER: ${{ (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository) && ' and not test(/(_ssh_|remote_server)/)' || '' }}
EXCLUDE_REMOTE_SERVER_TESTS_FILTER: " and not test(/remote_server/)"
strategy:
fail-fast: false
matrix:
@@ -131,7 +133,7 @@ jobs:
contents: 'read'
id-token: 'write'
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
- uses: ./.github/actions/prepare_environment
with:
@@ -204,7 +206,7 @@ jobs:
- name: Install cargo nextest
if: ${{ matrix.is_self_hosted == false }}
uses: taiki-e/install-action@9a29ce630c67077a359246f3e4f84941e05f28b5 # v1
uses: taiki-e/install-action@65851e10cd6c377f11a60e600abc07cb08643468 # v2.79.3
with:
tool: nextest
@@ -282,7 +284,7 @@ jobs:
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.EXCLUDE_SSH_TESTS_FILTER }}"
run: cargo nextest run ${{ env.WORKSPACE_TEST_ARGS }} ${{ matrix.extra_test_args }} -E "package(integration) and not test(shell_integration_tests)${{ env.EXCLUDE_SSH_TESTS_FILTER }}${{ env.EXCLUDE_REMOTE_SERVER_TESTS_FILTER }}"
env:
# We run shell-agnostic tests against zsh, as it has the shortest
# bootstrap times and tends to be the most reliable.
@@ -307,7 +309,7 @@ jobs:
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.EXCLUDE_SSH_TESTS_FILTER }}"
run: cargo nextest run ${{ env.WORKSPACE_TEST_ARGS }} ${{ matrix.extra_test_args }} -E "package(integration) and test(shell_integration_tests)${{ env.EXCLUDE_SSH_TESTS_FILTER }}${{ env.EXCLUDE_REMOTE_SERVER_TESTS_FILTER }}"
env:
WARP_SHELL_PATH: ${{ steps.echo_shells_unix.outputs.default_bash_path }}
@@ -333,7 +335,7 @@ jobs:
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.EXCLUDE_SSH_TESTS_FILTER }}"
run: cargo nextest run ${{ env.WORKSPACE_TEST_ARGS }} ${{ matrix.extra_test_args }} -E "package(integration) and test(shell_integration_tests)${{ env.EXCLUDE_SSH_TESTS_FILTER }}${{ env.EXCLUDE_REMOTE_SERVER_TESTS_FILTER }}"
env:
WARP_SHELL_PATH: ${{ steps.echo_shells_unix.outputs.latest_bash_path }}
@@ -356,7 +358,7 @@ jobs:
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.EXCLUDE_SSH_TESTS_FILTER }}"
run: cargo nextest run ${{ env.WORKSPACE_TEST_ARGS }} ${{ matrix.extra_test_args }} -E "package(integration) and test(shell_integration_tests)${{ env.EXCLUDE_SSH_TESTS_FILTER }}${{ env.EXCLUDE_REMOTE_SERVER_TESTS_FILTER }}"
env:
WARP_SHELL_PATH: ${{ steps.echo_shells_unix.outputs.fish_path }}
@@ -379,7 +381,7 @@ jobs:
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.EXCLUDE_SSH_TESTS_FILTER }}"
run: cargo nextest run ${{ env.WORKSPACE_TEST_ARGS }} ${{ matrix.extra_test_args }} -E "package(integration) and test(shell_integration_tests)${{ env.EXCLUDE_SSH_TESTS_FILTER }}${{ env.EXCLUDE_REMOTE_SERVER_TESTS_FILTER }}"
env:
WARP_SHELL_PATH: ${{ steps.echo_shells_unix.outputs.zsh_path }}
@@ -402,7 +404,7 @@ jobs:
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.EXCLUDE_SSH_TESTS_FILTER }}"
run: cargo nextest run ${{ env.WORKSPACE_TEST_ARGS }} ${{ matrix.extra_test_args }} -E "package(integration) and test(shell_integration_tests)${{ env.EXCLUDE_SSH_TESTS_FILTER }}${{ env.EXCLUDE_REMOTE_SERVER_TESTS_FILTER }}"
env:
WARP_SHELL_PATH: ${{ steps.echo_shells_unix.outputs.powershell_path }}
@@ -441,6 +443,94 @@ jobs:
# 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
remote-server-tests:
name: Run Linux remote-server integration tests
# Temporarily disabled while reconsidering the approach.
if: false
timeout-minutes: 25
runs-on: ubuntu-latest-large
needs: params
concurrency:
# The dedicated VM uses a fixed binary path, so serialize remote-server
# deploy/test jobs across workflow runs to avoid cross-run clobbering.
group: remote-server-test-vm
cancel-in-progress: false
permissions:
contents: 'read'
id-token: 'write'
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
- uses: ./.github/actions/prepare_environment
with:
target_os: linux
is_self_hosted: false
install_test_deps: true
- name: Install Shells
uses: ConorMacBride/install-package@3e7ad059e07782ee54fa35f827df52aae0626f30 # v1
with:
apt: zsh fish
- name: Echo default Bash
id: echo_bash
shell: bash
run: |
DEFAULT_BASH_PATH="$(command -pv bash)"
echo "default_bash_path=$DEFAULT_BASH_PATH" >> $GITHUB_OUTPUT
DEFAULT_BASH_VERSION="$($DEFAULT_BASH_PATH --version)"
echo "::notice title=Remote Server Tests - Default Bash Version::$DEFAULT_BASH_VERSION"
- name: Install cargo nextest
uses: taiki-e/install-action@65851e10cd6c377f11a60e600abc07cb08643468 # v2.79.3
with:
tool: nextest
- name: Set up gcloud authentication for remote-server 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 remote server deploy dependencies
shell: bash
run: |
sudo apt-get install -y curl sshpass xz-utils
MUSL_CROSS_TARGET="x86_64-unknown-linux-musl"
source script/linux/configure_musl_toolchain "$MUSL_CROSS_TARGET"
MUSL_CROSS_BIN="$(dirname "$WARP_MUSL_CC")"
echo "$MUSL_CROSS_BIN" >> "$GITHUB_PATH"
rustup target add x86_64-unknown-linux-musl
- name: Deploy remote server binary to test VM
run: script/deploy_remote_server_to_test_vm
- name: Run remote-server integration tests
uses: coactions/setup-xvfb@b6b4fcfb9f5a895edadc3bc76318fae0ac17c8b3 # v1
with:
run: cargo nextest run ${{ env.WORKSPACE_TEST_ARGS }} -E "package(integration) and test(/remote_server/)"
env:
WARP_SHELL_PATH: ${{ steps.echo_bash.outputs.default_bash_path }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Upload results of remote-server integration tests to trunk.io
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=integration,category=remote-server
variant: linux
use-cache: true
database-migration:
name: Database Migration (Diesel)
timeout-minutes: 5
@@ -449,7 +539,7 @@ jobs:
if: ${{ needs.params.outputs.affects-database-schema == 'true' }}
steps:
- name: Checkout sources
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
- name: Install cargo-binstall
uses: cargo-bins/cargo-binstall@dc19f1e48450eefe5a29b8da6c6b00a87d730b37 # v1.18.1
@@ -503,7 +593,7 @@ jobs:
needs: params
# if: ${{ needs.params.outputs.affects-rust-sources == 'true' }}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
- uses: ./.github/actions/prepare_environment
with:
@@ -514,8 +604,9 @@ jobs:
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 ./script/format
shell: bash
run: ./script/format --check
- name: Run cargo clippy
shell: bash
@@ -554,7 +645,7 @@ jobs:
needs: params
steps:
- name: Checkout sources
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
- uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
with:
@@ -589,6 +680,20 @@ jobs:
- name: Validate repo-sync markers
uses: warpdotdev/repo-sync/actions/validate-markers@main
- name: Check for incorrectly named test files
run: |
# Test files should end in _tests.rs, not _test.rs.
bad_files=$(find . -name '*_test.rs')
if [ -n "$bad_files" ]; then
while IFS= read -r f; do
echo "::error file=$f::Test file should be named ${f/_test.rs/_tests.rs} (use _tests.rs, not _test.rs)"
done <<< "$bad_files"
exit 1
fi
- name: Check for inline Rust test modules
run: ./script/check_no_inline_test_modules
wasm-lint:
name: Formatting + Clippy (wasm)
timeout-minutes: 20
@@ -603,7 +708,7 @@ jobs:
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: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
- uses: ./.github/actions/prepare_environment
with:
@@ -614,8 +719,15 @@ jobs:
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: Check Rust formatting
shell: bash
run: |
# TODO(vorporeal): Once people have gotten used to ./script/format, in a week or so,
# we can have CI enforce the new formatting.
#./script/format --check
# Until then, we'll keep running the traditional formatting check.
cargo fmt --check
- name: Run cargo clippy
run: |
@@ -658,7 +770,7 @@ jobs:
runs-on: ${{ matrix.runner }}
needs: params
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
- uses: ./.github/actions/prepare_environment
with:
@@ -1,16 +0,0 @@
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 }}
@@ -1,34 +0,0 @@
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 }}
OSS_WARP_API_KEY: ${{ secrets.OSS_WARP_API_KEY }}
@@ -1,34 +0,0 @@
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 }}
OSS_WARP_API_KEY: ${{ secrets.OSS_WARP_API_KEY }}
+132 -68
View File
@@ -73,7 +73,7 @@ jobs:
should_publish: ${{ steps.set_publish.outputs.should_publish }}
steps:
- name: Checkout sources
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
- name: Get channel configuration
id: get-config
@@ -111,6 +111,9 @@ jobs:
shell: bash
env:
CHANNEL: ${{ steps.get-config.outputs.channel }}
# See https://github.com/orgs/community/discussions/151442 for why we need to use
# a PAT here.
GITHUB_TOKEN: ${{ secrets.CREATE_RELEASE_TAG_PUSH_PAT }}
- name: Create GitHub release
if: ${{ steps.set_publish.outputs.should_publish == 'true' }}
@@ -150,7 +153,7 @@ jobs:
- arch: x86_64
dmg_name_suffix: x86_64
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
- uses: ./.github/actions/prepare_environment
with:
@@ -270,7 +273,7 @@ jobs:
if: ${{ inputs.build_macos != false }}
timeout-minutes: 60
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
- uses: ./.github/actions/prepare_environment
with:
@@ -417,7 +420,7 @@ jobs:
- arch: aarch64
- arch: x86_64
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
- uses: ./.github/actions/prepare_environment
with:
@@ -513,14 +516,17 @@ jobs:
runs-on: namespace-profile-ubuntu-20-04
needs: prepare_release
if: ${{ inputs.build_linux != false }}
timeout-minutes: 60
timeout-minutes: 90
env:
# Automatically extract AppImages before running them instead of mounting
# them with FUSE, which isn't available on GitHub runners (and this is
# easier and less error-prone than trying to install it).
APPIMAGE_EXTRACT_AND_RUN: "1"
# Cache the generated settings schema so prepare_bundled_resources only
# compiles and runs the generator once per job instead of per-package.
SETTINGS_SCHEMA_CACHE: ${{ github.workspace }}/.settings_schema_cache.json
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
- uses: ./.github/actions/prepare_environment
with:
@@ -588,7 +594,10 @@ jobs:
# Namespace's User Bundled Cache persists target/ between jobs on the same profile, which can
# leave stale packages from a previous channel's build in the linux bundle output dir.
- name: Clean stale bundle output
run: rm -rf target/*/bundle/linux
run: |
if [[ -d target ]]; then
find target -path '*/bundle/linux' -type d -prune -exec rm -rf {} +
fi
shell: bash
- name: Bundle app
@@ -665,12 +674,16 @@ jobs:
release_linux_cli_x86:
name: Build Release (Linux CLI x86_64)
runs-on: namespace-profile-ubuntu-20-04
runs-on: namespace-profile-ubuntu-22-04
needs: prepare_release
if: ${{ inputs.build_linux != false }}
timeout-minutes: 60
timeout-minutes: 90
env:
# Cache the generated settings schema so prepare_bundled_resources only
# compiles and runs the generator once per job instead of per-package.
SETTINGS_SCHEMA_CACHE: ${{ github.workspace }}/.settings_schema_cache.json
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
- uses: ./.github/actions/prepare_environment
with:
@@ -735,7 +748,10 @@ jobs:
# Namespace's User Bundled Cache persists target/ between jobs on the same profile, which can
# leave stale packages from a previous channel's build in the linux bundle output dir.
- name: Clean stale bundle output
run: rm -rf target/*/bundle/linux
run: |
if [[ -d target ]]; then
find target -path '*/bundle/linux' -type d -prune -exec rm -rf {} +
fi
shell: bash
- name: Bundle CLI
@@ -839,14 +855,17 @@ jobs:
runs-on: namespace-profile-ubuntu-20-04-arm
needs: prepare_release
if: ${{ inputs.build_linux != false }}
timeout-minutes: 60
timeout-minutes: 90
env:
# Automatically extract AppImages before running them instead of mounting
# them with FUSE, which isn't available on GitHub runners (and this is
# easier and less error-prone than trying to install it).
APPIMAGE_EXTRACT_AND_RUN: "1"
# Cache the generated settings schema so prepare_bundled_resources only
# compiles and runs the generator once per job instead of per-package.
SETTINGS_SCHEMA_CACHE: ${{ github.workspace }}/.settings_schema_cache.json
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
- uses: ./.github/actions/prepare_environment
with:
@@ -930,12 +949,12 @@ jobs:
build_linux_cli_arm_binaries:
name: Build Release (Linux CLI ARM)
runs-on: namespace-profile-ubuntu-20-04-arm
runs-on: namespace-profile-ubuntu-22-04
needs: prepare_release
if: ${{ inputs.build_linux != false }}
timeout-minutes: 60
timeout-minutes: 90
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
- uses: ./.github/actions/prepare_environment
with:
@@ -976,7 +995,7 @@ jobs:
id: build_cli
run: |
# Build the CLI only
script/bundle --channel $CHANNEL --artifact cli --packages none
script/bundle --channel $CHANNEL --artifact cli --packages none --arch aarch64
shell: bash
env:
CHANNEL: ${{ steps.get-config.outputs.channel }}
@@ -1011,9 +1030,13 @@ jobs:
needs: [ prepare_release, build_linux_arm_binaries, build_linux_cli_arm_binaries ]
if: ${{ inputs.build_linux != false }}
timeout-minutes: 60
env:
# Cache the generated settings schema so prepare_bundled_resources only
# compiles and runs the generator once per job instead of per-package.
SETTINGS_SCHEMA_CACHE: ${{ github.workspace }}/.settings_schema_cache.json
steps:
- name: Checkout sources
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
ref: ${{ needs.prepare_release.outputs.release_branch }}
@@ -1269,7 +1292,7 @@ jobs:
if: ${{ inputs.build_web != false }}
timeout-minutes: 60
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
- uses: ./.github/actions/prepare_environment
with:
@@ -1394,7 +1417,7 @@ jobs:
TRUSTED_SIGNING_ACCOUNT: warpdotdev
TRUSTED_SIGNING_CERT_PROFILE: warpterminal
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
- uses: ./.github/actions/prepare_environment
with:
@@ -1615,7 +1638,7 @@ jobs:
- release_web
- release_windows
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
name: Checkout sources
with:
# Fetch history for all tags and branches so we can compare revisions
@@ -1629,75 +1652,116 @@ jobs:
config_file: ${{ env.CONFIG_FILE }}
channel: ${{ inputs.channel }}
- name: Obtain a GitHub App Installation Access Token
# Stable releases use the Oz changelog-draft agent for higher-quality,
# human-reviewable output. Non-stable channels (dev/preview/beta) use the
# legacy generate-changelog action to avoid spending Oz agent tokens on
# daily dev cuts and preview RCs.
- name: Generate changelog via Oz (stable only)
if: inputs.channel == 'stable'
uses: warpdotdev/oz-agent-action@ce1621abf6a8ed8afdd4e4cc994545ede8fe1c6f # main
with:
prompt: |
Generate a changelog draft for the ${{ inputs.channel }} channel, release tag ${{ needs.prepare_release.outputs.release_tag }}.
Output directory: ${{ runner.temp }}/changelog-draft
Follow the workflow in .agents/skills/changelog-draft/SKILL.md exactly.
Make sure to produce both output files: changelog-draft.md and changelog-draft.json.
The release workflow may run from warpdotdev/warp-internal. When fetching PR data, pass the checked-out repository ("${{ github.repository }}") to fetch_prs.py and rely on the script's repo-sync normalization to resolve public warpdotdev/warp PR numbers, URLs, and authors. The script intentionally omits non-repo-sync PRs from warp-internal because they are private internal changes. Do not infer or synthesize public PR links manually.
After writing the output files, print the full contents of changelog-draft.md to stdout so it appears in the workflow log.
You are running in a GitHub Actions workflow. The repo is checked out at the default branch (HEAD) with full history. Use the release_tag input as the git range endpoint — do NOT check out the release tag. `gh` is authenticated. Do not commit, push, or create PRs.
warp_api_key: ${{ secrets.WARP_API_KEY }}
share: team
- name: Upload raw Markdown changelog artifact (stable only)
if: inputs.channel == 'stable'
id: upload_changelog_draft_markdown
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
with:
name: changelog-draft-markdown
path: ${{ runner.temp }}/changelog-draft/changelog-draft.md
if-no-files-found: error
- name: Convert draft JSON to release format (stable only)
if: inputs.channel == 'stable'
shell: bash
run: |
python3 .agents/skills/changelog-draft/scripts/convert_to_release_json.py \
--input "${{ runner.temp }}/changelog-draft/changelog-draft.json" \
--output "${{ runner.temp }}/changelog-draft/changelog-release.json"
- name: Obtain a GitHub App Installation Access Token (non-stable only)
if: inputs.channel != 'stable'
id: github_app_auth
run: |
TOKEN="$(npx obtain-github-app-installation-access-token ci ${{ secrets.GH_APP_CREDENTIALS_TOKEN }})"
echo "::add-mask::$TOKEN"
echo "token=$TOKEN" >> $GITHUB_OUTPUT
- name: Generate changelog
uses: warpdotdev/generate-changelog@main
id: generate_changelog
- name: Generate changelog via legacy action (non-stable only)
if: inputs.channel != 'stable'
id: legacy_changelog
uses: warpdotdev/generate-changelog@70f534c1e030dafb45046ae57e4aa4d43a2f5c84 # main
with:
channel: ${{ inputs.channel }}
github_auth_token: ${{ steps.github_app_auth.outputs.token }}
version: ${{ needs.prepare_release.outputs.release_tag }}
- name: Load changelog into step output
id: generate_changelog
shell: bash
env:
LEGACY_CHANGELOG: ${{ steps.legacy_changelog.outputs.changelog }}
run: |
# Bridge step: picks whichever generator ran for this channel and
# re-emits the changelog as outputs.changelog so downstream Slack/GCS
# steps work unchanged.
if [[ "${{ inputs.channel }}" == "stable" ]]; then
CHANGELOG_FILE="${{ runner.temp }}/changelog-draft/changelog-release.json"
if [ ! -f "$CHANGELOG_FILE" ]; then
echo "::error::changelog-release.json not found at $CHANGELOG_FILE"
exit 1
fi
jq empty "$CHANGELOG_FILE"
{
echo "changelog<<CHANGELOG_EOF"
cat "$CHANGELOG_FILE"
echo "CHANGELOG_EOF"
} >> $GITHUB_OUTPUT
else
echo "$LEGACY_CHANGELOG" | jq empty
{
echo "changelog<<CHANGELOG_EOF"
printf '%s\n' "$LEGACY_CHANGELOG"
echo "CHANGELOG_EOF"
} >> $GITHUB_OUTPUT
fi
- name: Build Slack changelog payload
id: build_slack_payload
shell: bash
env:
CHANGELOG_MD: ${{ steps.generate_changelog.outputs.changelog }}
CHANGELOG_JSON: ${{ steps.generate_changelog.outputs.changelog }}
RELEASE_TAG: ${{ needs.prepare_release.outputs.release_tag }}
MARKDOWN_ARTIFACT_URL: ${{ steps.upload_changelog_draft_markdown.outputs.artifact-url }}
run: |
# Rename the keys in the Changelog JSON to something more human readable.
# This is the changelog that goes to slack so it doesn't need to be extremely filtered.
NEW_CHANGELOG=$(echo $CHANGELOG_MD | jq 'with_entries(if .key == "newFeatures" then .key = "New Features" else . end)' | jq 'with_entries(if .key == "improvements" then .key = "Improvements" else . end)' | jq 'with_entries(if .key == "bugFixes" then .key = "Bug Fixes" else . end)' | jq 'with_entries(if .key == "images" then .key = "Image" else . end)')
# Generate the full markdown text as individual lines.
MARKDOWN=$(echo "$NEW_CHANGELOG" | jq -r 'to_entries[] | select(.value | length > 0) | "*\(.key)*", (.value[] | " \u2022 \(.)")')
if [ -z "$MARKDOWN" ]; then
CHANGELOG_INPUT="${{ runner.temp }}/slack-changelog-input.json"
SLACK_PAYLOAD="${{ runner.temp }}/slack-changelog-payload.json"
printf '%s\n' "$CHANGELOG_JSON" > "$CHANGELOG_INPUT"
python3 .agents/skills/changelog-draft/scripts/build_slack_payload.py \
--input "$CHANGELOG_INPUT" \
--release-tag "$RELEASE_TAG" \
--markdown-artifact-url "$MARKDOWN_ARTIFACT_URL" \
--output "$SLACK_PAYLOAD"
if ! jq -e '.blocks | length > 0' "$SLACK_PAYLOAD" >/dev/null; then
echo "has_content=" >> $GITHUB_OUTPUT
exit 0
fi
echo "has_content=true" >> $GITHUB_OUTPUT
# Split markdown into chunks of <= 3000 characters at newline boundaries.
# Each chunk will become its own section block in the Slack payload.
CHUNKS='[]'
BUFFER=""
while IFS= read -r line; do
if [ -z "$BUFFER" ]; then
CANDIDATE="$line"
else
CANDIDATE="$BUFFER"
CANDIDATE+=$'\n'
CANDIDATE+="$line"
fi
if [ ${#CANDIDATE} -gt 3000 ]; then
# Flush the current buffer as a chunk (if non-empty), start a new one with this line.
if [ -n "$BUFFER" ]; then
CHUNKS=$(echo "$CHUNKS" | jq --arg chunk "$BUFFER" '. + [$chunk]')
fi
BUFFER="$line"
else
BUFFER="$CANDIDATE"
fi
done <<< "$MARKDOWN"
# Flush the remaining buffer.
if [ -n "$BUFFER" ]; then
CHUNKS=$(echo "$CHUNKS" | jq --arg chunk "$BUFFER" '. + [$chunk]')
fi
# Build the full Slack Block Kit payload: header block + one section block per chunk.
PAYLOAD=$(echo "$CHUNKS" | jq -c \
--arg version "$RELEASE_TAG" \
'{blocks: ([{type: "header", text: {type: "plain_text", text: ("Changelog for " + $version)}}] + [.[] | {type: "section", text: {type: "mrkdwn", text: .}}])}')
echo "payload<<SLACK_PAYLOAD_EOF" >> $GITHUB_OUTPUT
echo "$PAYLOAD" >> $GITHUB_OUTPUT
cat "$SLACK_PAYLOAD" >> $GITHUB_OUTPUT
echo "SLACK_PAYLOAD_EOF" >> $GITHUB_OUTPUT
- name: Post to a Slack channel
@@ -1728,7 +1792,7 @@ jobs:
NEW_FEATURES=$(echo $CHANGELOG_SECTIONS | jq 'with_entries(select(.key == "New features"))' | jq -r 'to_entries[] | "* \(.value[])"' | awk -v ORS='\n' '1')
IMPROVEMENTS=$(echo $CHANGELOG_SECTIONS | jq 'with_entries(select(.key == "Improvements"))' | jq -r 'to_entries[] | "* \(.value[])"' | awk -v ORS='\n' '1')
# Extract the image URL from the list, and use the latest URL even if there are multiple
IMAGE=$(echo $CHANGELOG | jq -r '.image | if length > 0 then .[-1] else "" end')
IMAGE=$(echo $CHANGELOG | jq -r '.images | if length > 0 then .[-1] else "" end')
# Extract Oz updates as a JSON array
OZ_UPDATES=$(echo $CHANGELOG | jq -c '.oz_updates // []')
# Tweak the structure of the JSON, add in a top-level date field, and add in the markdown_sections field
+1 -1
View File
@@ -40,7 +40,7 @@ jobs:
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
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
- id: get-config
run: |
# Check to see if this was auto-run as a cron job. If so, set the
+1 -2
View File
@@ -89,10 +89,9 @@ jobs:
shell: bash
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
ref: ${{ inputs.branch }}
fetch-depth: 0
- name: Rename branch to deleted/
env:
@@ -45,7 +45,7 @@ jobs:
&& github.actor != 'github-actions[bot]'
runs-on: namespace-profile-ubuntu-20-04
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
# Check out the appropriate branch based on trigger type
# TODO: do this in sdk?
@@ -1,39 +0,0 @@
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 }}
OSS_WARP_API_KEY: ${{ secrets.OSS_WARP_API_KEY }}
+10 -3
View File
@@ -63,7 +63,7 @@ jobs:
* "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 }}
warp_api_key: ${{ secrets.OSS_WARP_API_KEY }}
share: team
- name: Upload feature flag log
@@ -178,7 +178,7 @@ jobs:
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 }}
warp_api_key: ${{ secrets.OSS_WARP_API_KEY }}
- name: Generate cleanup patch
env:
@@ -227,11 +227,18 @@ jobs:
run: |
git apply --binary --whitespace=nowarn "$RUNNER_TEMP/cleanup.patch"
- name: Generate GitHub App token
id: app-token
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1
with:
app-id: ${{ secrets.OZ_MGMT_GHA_APP_ID }}
private-key: ${{ secrets.OZ_MGMT_GHA_PRIVATE_KEY }}
- name: Create Pull Request
id: create_pr
uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v8.1.1
with:
token: ${{ github.token }}
token: ${{ steps.app-token.outputs.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 }}"
@@ -0,0 +1,66 @@
# ======================================================================================
# Workflow: Label External Contributors
# ======================================================================================
# Usage:
# - Runs whenever a pull request is opened.
# - Adds the `external-contributor` label to the PR if the PR head repository is
# a fork (i.e. it does not belong to the same repository as the base), and the
# PR is not authored by a bot.
#
# Notes:
# - The workflow triggers on `pull_request_target` rather than `pull_request` so
# that it has the `pull-requests: write` permission needed to apply labels even
# when the PR is opened from a fork. Because we never check out the PR's code
# and only read the event payload, this trigger is safe.
# ======================================================================================
name: Label External Contributors
on:
pull_request_target:
types: [opened]
# Default to a read-only token. The job below widens permissions explicitly.
permissions:
contents: read
jobs:
label-external-contributor:
name: Label external-contributor PRs
runs-on: ubuntu-latest
permissions:
pull-requests: write
steps:
- name: Determine and apply external-contributor label
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with:
script: |
const pr = context.payload.pull_request;
const author = pr.user.login;
// Ignore PRs authored by bots.
if (pr.user.type === 'Bot' || author.endsWith('[bot]')) {
console.log(`Skipping bot user: ${author}`);
return;
}
// The PR comes from a fork if its head repo differs from its base repo.
const isFork =
!pr.head.repo ||
pr.head.repo.full_name !== pr.base.repo.full_name;
console.log(
`PR #${pr.number} by ${author}: isFork=${isFork}`,
);
if (!isFork) {
return;
}
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: pr.number,
labels: ['external-contributor'],
});
console.log(`Labeled PR #${pr.number} as external-contributor`);
@@ -1,20 +0,0 @@
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 }}
@@ -1,24 +0,0 @@
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 }}
OSS_WARP_API_KEY: ${{ secrets.OSS_WARP_API_KEY }}
@@ -1,22 +0,0 @@
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 }}
OSS_WARP_API_KEY: ${{ secrets.OSS_WARP_API_KEY }}
-129
View File
@@ -1,129 +0,0 @@
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
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 }}
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_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", "")
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(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 }}
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 }}
OSS_WARP_API_KEY: ${{ secrets.OSS_WARP_API_KEY }}
@@ -0,0 +1,57 @@
# ======================================================================================
# Workflow: Stale Requested-Changes PRs
# ======================================================================================
# Usage:
# - Runs daily and follows up on external-contributor PRs that have an active
# requested-changes review and have gone inactive.
# - Posts reminder comments at 7 and 10 days of author inactivity, then closes
# the PR at 14 days — but only after the day-10 final warning has been posted.
# - Inactivity is author-driven: a head-branch push (GitHub-recorded) or a comment by
# the PR author resets the timer; maintainer/third-party/bot comments do not.
# - Reminder progress is tracked via marker comments authored by github-actions[bot],
# so no datastore is needed.
#
# Modes (workflow_dispatch `mode` input; scheduled runs use `full`):
# - dry-run: log eligible PRs and intended actions, write nothing.
# - reminder-only: post reminders, never close.
# - full: post reminders and close.
# ======================================================================================
name: Stale Requested-Changes PRs
on:
schedule:
- cron: '7 12 * * *' # 12:07 UTC daily (minute 7 avoids top-of-hour scheduler congestion)
workflow_dispatch:
inputs:
mode:
description: 'Write mode'
type: choice
default: full
options:
- dry-run
- reminder-only
- full
permissions:
contents: read
pull-requests: write
issues: write
jobs:
stale-requested-changes:
runs-on: ubuntu-latest
steps:
- name: Check out repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
# Only the follow-up script is needed; skip blob content for speed.
sparse-checkout: .github/scripts
filter: blob:none
- name: Follow up on stale requested-changes PRs
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with:
script: |
const run = require('./.github/scripts/stale-requested-changes-prs.js')
await run({ github, context })
@@ -1,60 +0,0 @@
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 }}
OSS_WARP_API_KEY: ${{ secrets.OSS_WARP_API_KEY }}
@@ -1,22 +0,0 @@
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 }}
OSS_WARP_API_KEY: ${{ secrets.OSS_WARP_API_KEY }}
@@ -1,18 +0,0 @@
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 }}
OSS_WARP_API_KEY: ${{ secrets.OSS_WARP_API_KEY }}