Initial public release of Warp.

Repo-Sync-Origin: warpdotdev/warp-internal@12af1d983b
This commit is contained in:
David Stern
2026-04-28 08:43:33 -05:00
commit 0dbd3d567a
4982 changed files with 1431549 additions and 0 deletions
+19
View File
@@ -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.
+162
View File
@@ -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"
+720
View File
@@ -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!"
+75
View File
@@ -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
+93
View File
@@ -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
+111
View File
@@ -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 }}
+261
View File
@@ -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 }}
+74
View File
@@ -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 }}
+141
View File
@@ -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 }}
+32
View File
@@ -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 }}
+23
View File
@@ -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 }}
+23
View File
@@ -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'
});
}