Initial public release of Warp.
Repo-Sync-Origin: warpdotdev/warp-internal@12af1d983b
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
# Release Configurations
|
||||
|
||||
This README file documents the format of the `release_configurations.json` file located in this directory. The file defines Warp's various release channels, and provides values for the various variables that are necessary to run the `create_new_releases.yml` GitHub workflow.
|
||||
|
||||
At some point, we may want to replace this document with a JSON schema file (which could be used to validate the correctness of the configuration as part of PR presubmit).
|
||||
|
||||
## Fields
|
||||
|
||||
* **channel**: The channel's unique identifier
|
||||
* **type**: The release cadence. At present, the valid values are "nightly" or "weekly".
|
||||
* **is_prerelease**: If true, the GitHub release for this channel will be marked as prerelease.
|
||||
* **is_autopush**: If true, this channel uses the "latest" keyword in `channel_versions.json` to automatically deploy new release candidates. Non-autopush channels require a manual change in order to deploy them.
|
||||
* **release_base_name**: The base name of GitHub releases created for this channel.
|
||||
* **release_body_text**: The body text for GitHub releases created for this channel.
|
||||
* **sentry_project**: Which Sentry project should receive crash and error reports for this channel.
|
||||
* **sentry_environment**: The Sentry environment that corresponds to this channel.
|
||||
* **changelog_slack_channel**: The Slack channel where new changelogs will be posted whenever a new release candidates is cut.
|
||||
* **gcs_cache_control_value**: The value of the cache-control response header for release DMGs.
|
||||
- **IMPORTANT!!**: the value of the cache-control header _must_ be all lowercase; uppercase values will not be respected by Cloud CDN.
|
||||
@@ -0,0 +1,162 @@
|
||||
name: Check Approvals
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, synchronize, ready_for_review]
|
||||
pull_request_review:
|
||||
types: [submitted]
|
||||
|
||||
jobs:
|
||||
check_owners:
|
||||
name: Check OWNERS approval
|
||||
runs-on: ubuntu-latest
|
||||
# Skip draft PRs.
|
||||
if: github.event.pull_request.draft == false
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
# We don't actually need the contents of the files, just their names.
|
||||
filter: blob:none
|
||||
|
||||
- name: Get changed files
|
||||
id: changed_files
|
||||
run: |
|
||||
HEAD_SHA=${{ github.event.pull_request.head.sha }}
|
||||
BASE_SHA=${{ github.event.pull_request.base.sha }}
|
||||
MERGE_BASE=$(git merge-base "$BASE_SHA" "$HEAD_SHA")
|
||||
CHANGED_FILES=$(git diff --name-only "$MERGE_BASE" "$HEAD_SHA" | tr '\n' ' ')
|
||||
echo "files=$CHANGED_FILES" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Get PR approvers
|
||||
id: approvers
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
PR_NUMBER=${{ github.event.pull_request.number }}
|
||||
PR_AUTHOR=${{ github.event.pull_request.user.login }}
|
||||
|
||||
# Get all approved reviews.
|
||||
REVIEWERS=$(gh pr view "$PR_NUMBER" --json reviews --jq '[.reviews[] | select(.state == "APPROVED") | .author.login] | unique | join(" ")')
|
||||
|
||||
# Combine PR author (self-approves) with reviewers.
|
||||
ALL_APPROVERS="$PR_AUTHOR $REVIEWERS"
|
||||
echo "approvers=$ALL_APPROVERS" >> "$GITHUB_OUTPUT"
|
||||
echo "has_reviewers=$( [[ -n "$REVIEWERS" ]] && echo true || echo false )" >> "$GITHUB_OUTPUT"
|
||||
echo "PR author: $PR_AUTHOR"
|
||||
echo "Reviewers who approved: $REVIEWERS"
|
||||
echo "All approvers: $ALL_APPROVERS"
|
||||
|
||||
- name: Check OWNERS approval
|
||||
env:
|
||||
CHANGED_FILES: ${{ steps.changed_files.outputs.files }}
|
||||
APPROVERS: ${{ steps.approvers.outputs.approvers }}
|
||||
run: |
|
||||
set -e
|
||||
|
||||
# Convert approvers to an array.
|
||||
read -ra APPROVER_ARRAY <<< "$APPROVERS"
|
||||
|
||||
# Function to get owners for a file by walking up the directory tree.
|
||||
get_owners_for_file() {
|
||||
local file="$1"
|
||||
local dir
|
||||
dir=$(dirname "$file")
|
||||
local owners=()
|
||||
|
||||
# Walk up the directory tree.
|
||||
while [[ "$dir" != "." && "$dir" != "/" ]]; do
|
||||
if [[ -f "$dir/OWNERS" ]]; then
|
||||
# Read owners file, skip comments and empty lines.
|
||||
while IFS= read -r line || [[ -n "$line" ]]; do
|
||||
# Skip comments and empty lines.
|
||||
line=$(echo "$line" | sed 's/#.*//' | xargs)
|
||||
if [[ -n "$line" ]]; then
|
||||
owners+=("$line")
|
||||
fi
|
||||
done < "$dir/OWNERS"
|
||||
fi
|
||||
dir=$(dirname "$dir")
|
||||
done
|
||||
|
||||
# Check root directory.
|
||||
if [[ -f "OWNERS" ]]; then
|
||||
while IFS= read -r line || [[ -n "$line" ]]; do
|
||||
line=$(echo "$line" | sed 's/#.*//' | xargs)
|
||||
if [[ -n "$line" ]]; then
|
||||
owners+=("$line")
|
||||
fi
|
||||
done < "OWNERS"
|
||||
fi
|
||||
|
||||
# Return unique owners.
|
||||
printf '%s\n' "${owners[@]}" | sort -u | tr '\n' ' ' | sed 's/ $//'
|
||||
}
|
||||
|
||||
# Function to check if any approver is in the owners list.
|
||||
has_owner_approval() {
|
||||
local owners="$1"
|
||||
for approver in "${APPROVER_ARRAY[@]}"; do
|
||||
if echo "$owners" | grep -qw "$approver"; then
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
MISSING_APPROVAL=()
|
||||
FILES_CHECKED=0
|
||||
|
||||
for file in $CHANGED_FILES; do
|
||||
# Skip if file doesn't exist (deleted files).
|
||||
FILES_CHECKED=$((FILES_CHECKED + 1))
|
||||
|
||||
OWNERS=$(get_owners_for_file "$file")
|
||||
|
||||
if [[ -z "$OWNERS" ]]; then
|
||||
# No OWNERS files in ancestry - automatically passes.
|
||||
echo "✓ $file (no OWNERS requirement)"
|
||||
continue
|
||||
fi
|
||||
|
||||
if has_owner_approval "$OWNERS"; then
|
||||
echo "✓ $file"
|
||||
else
|
||||
echo "✗ $file (owners: $OWNERS)"
|
||||
MISSING_APPROVAL+=("$file")
|
||||
fi
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "Files checked: $FILES_CHECKED"
|
||||
echo "Approvers: ${APPROVER_ARRAY[*]}"
|
||||
|
||||
# Save missing files for comment step (must happen before exit).
|
||||
echo "MISSING_FILES<<EOF" >> "$GITHUB_ENV"
|
||||
for file in "${MISSING_APPROVAL[@]}"; do
|
||||
OWNERS=$(get_owners_for_file "$file")
|
||||
echo "- \`$file\` (needs approval from: $OWNERS)" >> "$GITHUB_ENV"
|
||||
done
|
||||
echo "EOF" >> "$GITHUB_ENV"
|
||||
|
||||
if [[ ${#MISSING_APPROVAL[@]} -gt 0 ]]; then
|
||||
echo ""
|
||||
echo "::error::The following files are missing OWNERS approval:"
|
||||
for file in "${MISSING_APPROVAL[@]}"; do
|
||||
OWNERS=$(get_owners_for_file "$file")
|
||||
echo "::error:: - $file (needs approval from: $OWNERS)"
|
||||
done
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "All files have required OWNERS approval."
|
||||
|
||||
- name: Comment on PR about missing approvals
|
||||
if: failure() && github.event_name == 'pull_request_review' && steps.approvers.outputs.has_reviewers == 'true'
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
PR_NUMBER=${{ github.event.pull_request.number }}
|
||||
gh pr comment "$PR_NUMBER" --body "The following files still need approval from their OWNERS:\n\n$MISSING_FILES"
|
||||
@@ -0,0 +1,720 @@
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- 'master'
|
||||
- '*_release/*'
|
||||
types:
|
||||
- opened
|
||||
- reopened
|
||||
- synchronize
|
||||
- ready_for_review
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
runner_type:
|
||||
required: true
|
||||
type: choice
|
||||
options:
|
||||
- self_hosted
|
||||
- github_hosted
|
||||
workflow_call:
|
||||
|
||||
name: Warp CI
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
NEXTEST_PROFILE: ci
|
||||
WORKSPACE_TEST_ARGS: --workspace --locked --exclude command-signatures-v2
|
||||
# Include only as much debug info as is necessary to see backtraces
|
||||
# in test failures. This should speed up compilation somewhat and
|
||||
# reduces the size of built artifacts (which addresses issues with
|
||||
# running out of space on GitHub-hosted runners).
|
||||
RUSTFLAGS: -C debuginfo=line-tables-only --cfg=web_sys_unstable_apis
|
||||
# Ensure we open up real windows that render their contents via the GPU,
|
||||
# so that we can exercise the actual rendering logic.
|
||||
WARPUI_USE_REAL_DISPLAY_IN_INTEGRATION_TESTS: 1
|
||||
|
||||
concurrency:
|
||||
# Cancel any outstanding CI workflow runs against the same PR.
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
params:
|
||||
name: Compute workflow parameters
|
||||
timeout-minutes: 3
|
||||
runs-on: ubuntu-latest
|
||||
# Don't automatically run CI for draft PRs, to reduce GitHub Actions costs.
|
||||
#
|
||||
# Also, don't run CI for repo-sync PRs _unless_ there is a merge conflict - for those,
|
||||
# we'll want to make sure that the conflict resolution doesn't introduce any issues.
|
||||
if: >-
|
||||
github.event.pull_request.draft == false &&
|
||||
(!startsWith(github.event.pull_request.head.ref, 'repo-sync/') || contains(github.event.pull_request.labels.*.name, 'repo-sync:conflict'))
|
||||
outputs:
|
||||
affects-database-schema: ${{ github.ref == 'master' || steps.filter.outputs.affects-database-schema }}
|
||||
affects-rust-sources: ${{ github.ref == 'master' || steps.filter.outputs.affects-rust-sources }}
|
||||
macos-runner: ${{ steps.mac_runner_type.outputs.value }}
|
||||
wasm-runner: ${{ steps.wasm_runner_type.outputs.value }}
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
|
||||
- name: Check changed files
|
||||
uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1
|
||||
id: filter
|
||||
with:
|
||||
filters: |
|
||||
affects-database-schema:
|
||||
- 'crates/persistence/src/schema.rs'
|
||||
- 'crates/persistence/migrations/**'
|
||||
affects-rust-sources:
|
||||
- '**.rs'
|
||||
|
||||
- name: Determine macOS runner type
|
||||
id: mac_runner_type
|
||||
run: |
|
||||
RUNNER_TYPE='["namespace-profile-mac-ci"]'
|
||||
echo "value=$RUNNER_TYPE" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Determine wasm runner type
|
||||
id: wasm_runner_type
|
||||
run: |
|
||||
# Use a larger (16 core, 64GB ram) runner on Linux to speed up execution time.
|
||||
# https://github.com/warpdotdev/warp-internal/settings/actions/runners
|
||||
RUNNER_TYPE='["ubuntu-latest-large"]'
|
||||
echo "value=$RUNNER_TYPE" >> $GITHUB_OUTPUT
|
||||
|
||||
tests:
|
||||
name: Run ${{ matrix.name }} tests
|
||||
timeout-minutes: 25
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- os: "macos"
|
||||
name: "MacOS"
|
||||
runner: ${{ fromJSON(needs.params.outputs.macos-runner) }}
|
||||
is_self_hosted: ${{ contains(fromJSON(needs.params.outputs.macos-runner), 'self-hosted') }}
|
||||
extra_test_args: ""
|
||||
|
||||
- os: "linux"
|
||||
name: "Linux"
|
||||
# Use a larger (16 core, 64GB ram) runner on Linux to speed up execution time.
|
||||
# https://github.com/warpdotdev/warp-internal/settings/actions/runners
|
||||
runner: ubuntu-latest-large
|
||||
# We don't (yet) have any self-hosted Linux runners.
|
||||
is_self_hosted: false
|
||||
extra_test_args: ""
|
||||
|
||||
- os: "windows"
|
||||
name: "Windows"
|
||||
runner: windows-latest-large
|
||||
# We don't (yet) have any self-hosted Windows runners.
|
||||
is_self_hosted: false
|
||||
extra_test_args: "--exclude command-signatures-v2 --exclude warp_js"
|
||||
runs-on: ${{ matrix.runner }}
|
||||
needs: params
|
||||
# Make sure an ID token is created with the necessary permissions to use
|
||||
# GCP's Workload Identity Federation (for service account authentication).
|
||||
permissions:
|
||||
contents: 'read'
|
||||
id-token: 'write'
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
|
||||
- uses: ./.github/actions/prepare_environment
|
||||
with:
|
||||
target_os: ${{ matrix.os }}
|
||||
is_self_hosted: ${{ matrix.is_self_hosted }}
|
||||
install_test_deps: true
|
||||
|
||||
- name: Install Shells
|
||||
uses: ConorMacBride/install-package@3e7ad059e07782ee54fa35f827df52aae0626f30 # v1
|
||||
if: ${{ matrix.is_self_hosted == false }}
|
||||
with:
|
||||
apt: zsh fish
|
||||
brew: fish bash
|
||||
|
||||
- name: Echo Shells (UNIX)
|
||||
id: echo_shells_unix
|
||||
if: ${{ matrix.os != 'windows' }}
|
||||
shell: bash
|
||||
run: |
|
||||
if ${{ matrix.os == 'macos' }}; then
|
||||
LATEST_BASH_PATH="$(brew --prefix bash)/bin/bash"
|
||||
echo "latest_bash_path=$LATEST_BASH_PATH" >> $GITHUB_OUTPUT
|
||||
LATEST_BASH_VERSION="$($LATEST_BASH_PATH --version)"
|
||||
echo "::notice title=${{ matrix.name }} Tests - Latest Bash Version::$LATEST_BASH_VERSION"
|
||||
fi
|
||||
|
||||
DEFAULT_BASH_PATH="$(command -pv bash)"
|
||||
DEFAULT_BASH_VERSION="$($DEFAULT_BASH_PATH --version)"
|
||||
echo "default_bash_path=$DEFAULT_BASH_PATH" >> $GITHUB_OUTPUT
|
||||
echo "::notice title=${{ matrix.name }} Tests - Default Bash Version::$DEFAULT_BASH_VERSION"
|
||||
|
||||
FISH_PATH="$(which fish)"
|
||||
echo "fish_path=$FISH_PATH" >> $GITHUB_OUTPUT
|
||||
FISH_VERSION="$($FISH_PATH --version)"
|
||||
echo "::notice title=${{ matrix.name }} Tests - Fish Version::$FISH_VERSION"
|
||||
|
||||
ZSH_VERSION="$(zsh --version)"
|
||||
ZSH_PATH="$(which zsh)"
|
||||
echo "zsh_path=$ZSH_PATH" >> $GITHUB_OUTPUT
|
||||
echo "::notice title=${{ matrix.name }} Tests - Zsh Version::$ZSH_VERSION"
|
||||
|
||||
POWERSHELL_PATH="$(which pwsh)"
|
||||
POWERSHELL_VERSION=$(pwsh -version | awk '{print $2}')
|
||||
echo "powershell_path=$POWERSHELL_PATH" >> $GITHUB_OUTPUT
|
||||
echo "::notice title=${{ matrix.name }} Tests - Powershell Version::$POWERSHELL_VERSION"
|
||||
|
||||
- name: Echo Shells (Windows)
|
||||
id: echo_shells_windows
|
||||
if: ${{ matrix.os == 'windows' }}
|
||||
shell: pwsh
|
||||
run: |
|
||||
$env:POWERSHELL_PATH = (Get-Command pwsh).Source
|
||||
$env:POWERSHELL_VERSION = (Get-Command pwsh).Version.Major
|
||||
Write-Output 'Powershell path:' $env:POWERSHELL_PATH
|
||||
Write-Output 'Powershell version:' $env:POWERSHELL_VERSION
|
||||
Write-Output "powershell_path=$env:POWERSHELL_PATH" >> $env:GITHUB_OUTPUT
|
||||
Write-Output "::notice title=${{ matrix.name }} Tests - Powershell Version::$env:POWERSHELL_VERSION"
|
||||
|
||||
- name: Echo Default Shell
|
||||
id: echo_default_shell
|
||||
shell: bash
|
||||
run: |
|
||||
if ${{ matrix.os == 'windows' }}; then
|
||||
DEFAULT_SHELL="${{ steps.echo_shells_windows.outputs.powershell_path }}"
|
||||
else
|
||||
DEFAULT_SHELL="${{ steps.echo_shells_unix.outputs.zsh_path }}"
|
||||
fi
|
||||
echo "Using default shell '$DEFAULT_SHELL'"
|
||||
echo "default_shell_path=$DEFAULT_SHELL" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Install cargo nextest
|
||||
if: ${{ matrix.is_self_hosted == false }}
|
||||
uses: taiki-e/install-action@9a29ce630c67077a359246f3e4f84941e05f28b5 # v1
|
||||
with:
|
||||
tool: nextest
|
||||
|
||||
- name: Run ssh-agent for SSH tests
|
||||
if: ${{ matrix.os == 'linux' }}
|
||||
run: |
|
||||
# Create the .ssh dir.
|
||||
mkdir -p ~/.ssh
|
||||
|
||||
# Run the agent.
|
||||
eval "$(ssh-agent -s)"
|
||||
|
||||
# Persist the SSH_AUTH_SOCK and SSH_AGENT_PID for future steps
|
||||
echo "SSH_AUTH_SOCK=$SSH_AUTH_SOCK" >> $GITHUB_ENV
|
||||
echo "SSH_AGENT_PID=$SSH_AGENT_PID" >> $GITHUB_ENV
|
||||
|
||||
- name: Set up gcloud authentication for SSH tests
|
||||
uses: google-github-actions/auth@7c6bc770dae815cd3e89ee6cdf493a5fab2cc093 # v3.0.0
|
||||
with:
|
||||
workload_identity_provider: projects/63595664881/locations/global/workloadIdentityPools/github-pool/providers/github-provider
|
||||
service_account: github-ci-workflow@warp-ssh-integration-testing.iam.gserviceaccount.com
|
||||
|
||||
- name: Install gcloud CLI tool
|
||||
uses: google-github-actions/setup-gcloud@aa5489c8933f4cc7a4f7d45035b3b1440c9c10db # v3.0.1
|
||||
with:
|
||||
version: '>= 397.0.0'
|
||||
|
||||
- name: Install vim for richer alt-screen tests
|
||||
if: ${{ matrix.is_self_hosted == false }}
|
||||
uses: ConorMacBride/install-package@3e7ad059e07782ee54fa35f827df52aae0626f30 # v1
|
||||
with:
|
||||
brew: vim
|
||||
apt: vim
|
||||
|
||||
- name: Compile tests
|
||||
run: cargo nextest run ${{ env.WORKSPACE_TEST_ARGS }} ${{ matrix.extra_test_args }} --no-run
|
||||
env:
|
||||
# Attach the GitHub auth token so that requests to the GitHub API
|
||||
# don't get rate limited during compilation (e.g.: retrieving
|
||||
# Sentry SDK binary releases).
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Run unit tests
|
||||
# Unlike later test steps, only run this if previous steps succeed (so
|
||||
# that we don't bother running these first tests if test compilation
|
||||
# fails).
|
||||
if: success()
|
||||
run: cargo nextest run ${{ env.WORKSPACE_TEST_ARGS }} ${{ matrix.extra_test_args }} -E "not package(integration)"
|
||||
env:
|
||||
# Unit tests may end up spawning a shell, but shouldn't care which
|
||||
# shell it is. We'll use zsh, as it has the shortest bootstrap times
|
||||
# and tends to be the most reliable.
|
||||
WARP_SHELL_PATH: ${{ steps.echo_default_shell.outputs.default_shell_path }}
|
||||
|
||||
- name: Upload results of unit tests to trunk.io
|
||||
# Run this step even when the tests fail. Skip if the workflow is cancelled.
|
||||
if: ${{ !cancelled() }}
|
||||
continue-on-error: true
|
||||
uses: trunk-io/analytics-uploader@95a0fb8b29e45b6068304261fb518644b426a803 # v2.0.8
|
||||
with:
|
||||
junit-paths: target/nextest/ci/junit.xml
|
||||
cli-version: 0.12.5
|
||||
org-slug: warp
|
||||
token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
tags: type=unit
|
||||
variant: ${{ matrix.os }}
|
||||
use-cache: true
|
||||
|
||||
- name: Run shell-agnostic integration tests
|
||||
# Run this step even if a previous test step fails
|
||||
if: matrix.os != 'windows' && (success() || failure())
|
||||
uses: coactions/setup-xvfb@b6b4fcfb9f5a895edadc3bc76318fae0ac17c8b3 # v1
|
||||
with:
|
||||
run: cargo nextest run ${{ env.WORKSPACE_TEST_ARGS }} ${{ matrix.extra_test_args }} -E "package(integration) and not test(shell_integration_tests)"
|
||||
env:
|
||||
# We run shell-agnostic tests against zsh, as it has the shortest
|
||||
# bootstrap times and tends to be the most reliable.
|
||||
WARP_SHELL_PATH: ${{ steps.echo_shells_unix.outputs.zsh_path }}
|
||||
|
||||
- name: Upload results of shell-agnostic integration tests to trunk.io
|
||||
# Run this step even when the tests fail. Skip if the workflow is cancelled.
|
||||
if: ${{ matrix.os != 'windows' && !cancelled() }}
|
||||
continue-on-error: true
|
||||
uses: trunk-io/analytics-uploader@95a0fb8b29e45b6068304261fb518644b426a803 # v2.0.8
|
||||
with:
|
||||
junit-paths: target/nextest/ci/junit.xml
|
||||
cli-version: 0.12.5
|
||||
org-slug: warp
|
||||
token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
tags: type=integration,category=shell-agnostic
|
||||
variant: ${{ matrix.os }}
|
||||
use-cache: true
|
||||
|
||||
- name: Run shell integration tests against default version of bash
|
||||
# Run this step even if a previous test step fails
|
||||
if: matrix.os != 'windows' && (success() || failure())
|
||||
uses: coactions/setup-xvfb@b6b4fcfb9f5a895edadc3bc76318fae0ac17c8b3 # v1
|
||||
with:
|
||||
run: cargo nextest run ${{ env.WORKSPACE_TEST_ARGS }} ${{ matrix.extra_test_args }} -E "package(integration) and test(shell_integration_tests)"
|
||||
env:
|
||||
WARP_SHELL_PATH: ${{ steps.echo_shells_unix.outputs.default_bash_path }}
|
||||
|
||||
- name: Upload results of shell integration tests against default version of bash to trunk.io
|
||||
# Run this step even when the tests fail. Skip if the workflow is cancelled.
|
||||
if: ${{ matrix.os != 'windows' && !cancelled() }}
|
||||
continue-on-error: true
|
||||
uses: trunk-io/analytics-uploader@95a0fb8b29e45b6068304261fb518644b426a803 # v2.0.8
|
||||
with:
|
||||
junit-paths: target/nextest/ci/junit.xml
|
||||
cli-version: 0.12.5
|
||||
org-slug: warp
|
||||
token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
tags: type=integration,category=bash-default
|
||||
variant: ${{ matrix.os }}
|
||||
use-cache: true
|
||||
|
||||
- name: Run shell integration tests against latest version of bash
|
||||
# Run this step even if a previous test step fails.
|
||||
# We only run this on MacOS since the default Bash version on Mac
|
||||
# was released in 2007. Most Linux distros ship with a relatively
|
||||
# new (version 5.0+) version of Bash.
|
||||
if: (success() || failure()) && runner.os == 'macos'
|
||||
uses: coactions/setup-xvfb@b6b4fcfb9f5a895edadc3bc76318fae0ac17c8b3 # v1
|
||||
with:
|
||||
run: cargo nextest run ${{ env.WORKSPACE_TEST_ARGS }} ${{ matrix.extra_test_args }} -E "package(integration) and test(shell_integration_tests)"
|
||||
env:
|
||||
WARP_SHELL_PATH: ${{ steps.echo_shells_unix.outputs.latest_bash_path }}
|
||||
|
||||
- name: Upload results of shell integration tests against latest version of bash to trunk.io
|
||||
# Run this step even when the tests fail. Skip if the workflow is cancelled.
|
||||
if: ${{ matrix.os != 'windows' && !cancelled() }}
|
||||
continue-on-error: true
|
||||
uses: trunk-io/analytics-uploader@95a0fb8b29e45b6068304261fb518644b426a803 # v2.0.8
|
||||
with:
|
||||
junit-paths: target/nextest/ci/junit.xml
|
||||
cli-version: 0.12.5
|
||||
org-slug: warp
|
||||
token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
tags: type=integration,category=bash-latest
|
||||
variant: ${{ matrix.os }}
|
||||
use-cache: true
|
||||
|
||||
- name: Run shell integration tests against fish
|
||||
# Run this step even if a previous test step fails
|
||||
if: matrix.os != 'windows' && (success() || failure())
|
||||
uses: coactions/setup-xvfb@b6b4fcfb9f5a895edadc3bc76318fae0ac17c8b3 # v1
|
||||
with:
|
||||
run: cargo nextest run ${{ env.WORKSPACE_TEST_ARGS }} ${{ matrix.extra_test_args }} -E "package(integration) and test(shell_integration_tests)"
|
||||
env:
|
||||
WARP_SHELL_PATH: ${{ steps.echo_shells_unix.outputs.fish_path }}
|
||||
|
||||
- name: Upload results of shell integration tests against fish to trunk.io
|
||||
# Run this step even when the tests fail. Skip if the workflow is cancelled.
|
||||
if: ${{ matrix.os != 'windows' && !cancelled() }}
|
||||
continue-on-error: true
|
||||
uses: trunk-io/analytics-uploader@95a0fb8b29e45b6068304261fb518644b426a803 # v2.0.8
|
||||
with:
|
||||
junit-paths: target/nextest/ci/junit.xml
|
||||
cli-version: 0.12.5
|
||||
org-slug: warp
|
||||
token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
tags: type=integration,category=fish
|
||||
variant: ${{ matrix.os }}
|
||||
use-cache: true
|
||||
|
||||
- name: Run shell integration tests against zsh
|
||||
# Run this step even if a previous test step fails
|
||||
if: matrix.os != 'windows' && (success() || failure())
|
||||
uses: coactions/setup-xvfb@b6b4fcfb9f5a895edadc3bc76318fae0ac17c8b3 # v1
|
||||
with:
|
||||
run: cargo nextest run ${{ env.WORKSPACE_TEST_ARGS }} ${{ matrix.extra_test_args }} -E "package(integration) and test(shell_integration_tests)"
|
||||
env:
|
||||
WARP_SHELL_PATH: ${{ steps.echo_shells_unix.outputs.zsh_path }}
|
||||
|
||||
- name: Upload results of shell integration tests against zsh to trunk.io
|
||||
# Run this step even when the tests fail. Skip if the workflow is cancelled.
|
||||
if: ${{ matrix.os != 'windows' && !cancelled() }}
|
||||
continue-on-error: true
|
||||
uses: trunk-io/analytics-uploader@95a0fb8b29e45b6068304261fb518644b426a803 # v2.0.8
|
||||
with:
|
||||
junit-paths: target/nextest/ci/junit.xml
|
||||
cli-version: 0.12.5
|
||||
org-slug: warp
|
||||
token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
tags: type=integration,category=zsh
|
||||
variant: ${{ matrix.os }}
|
||||
use-cache: true
|
||||
|
||||
- name: Run shell integration tests against powershell
|
||||
# Run this step even if a previous test step fails
|
||||
if: matrix.os != 'windows' && (success() || failure())
|
||||
uses: coactions/setup-xvfb@b6b4fcfb9f5a895edadc3bc76318fae0ac17c8b3 # v1
|
||||
with:
|
||||
run: cargo nextest run ${{ env.WORKSPACE_TEST_ARGS }} ${{ matrix.extra_test_args }} -E "package(integration) and test(shell_integration_tests)"
|
||||
env:
|
||||
WARP_SHELL_PATH: ${{ steps.echo_shells_unix.outputs.powershell_path }}
|
||||
|
||||
- name: Upload results of shell integration tests against powershell to trunk.io
|
||||
# Run this step even when the tests fail. Skip if the workflow is cancelled.
|
||||
if: ${{ matrix.os != 'windows' && !cancelled() }}
|
||||
continue-on-error: true
|
||||
uses: trunk-io/analytics-uploader@95a0fb8b29e45b6068304261fb518644b426a803 # v2.0.8
|
||||
with:
|
||||
junit-paths: target/nextest/ci/junit.xml
|
||||
cli-version: 0.12.5
|
||||
org-slug: warp
|
||||
token: ${{ secrets.TRUNK_API_TOKEN }}
|
||||
tags: type=integration,category=powershell
|
||||
variant: ${{ matrix.os }}
|
||||
use-cache: true
|
||||
|
||||
# Run doctests explicitly, as nextest doesn't yet support running them.
|
||||
# See: https://github.com/nextest-rs/nextest/issues/16
|
||||
- name: Run doc tests
|
||||
# Run this step even if a previous test step fails
|
||||
if: success() || failure()
|
||||
run: cargo test ${{ env.WORKSPACE_TEST_ARGS }} ${{ matrix.extra_test_args }} --doc
|
||||
|
||||
# Run warp_completer tests with the "v2" (completions-on-js) flag enabled. We do this to
|
||||
# ensure that the v2 completions implementation doesn't regress/rot while it's development
|
||||
# is paused.
|
||||
- name: Run completions-on-js tests
|
||||
# Run this step even if a previous test step fails
|
||||
if: matrix.os != 'windows' && (success() || failure())
|
||||
uses: coactions/setup-xvfb@b6b4fcfb9f5a895edadc3bc76318fae0ac17c8b3 # v1
|
||||
with:
|
||||
run: cargo nextest run --locked -p warp_completer --features v2
|
||||
|
||||
# This is longer than we like and temporary until we speed up integration tests
|
||||
# The maximum timeout for the entire job is 6 hours:
|
||||
# https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepstimeout-minutes
|
||||
|
||||
database-migration:
|
||||
name: Database Migration (Diesel)
|
||||
timeout-minutes: 5
|
||||
runs-on: ubuntu-latest
|
||||
needs: params
|
||||
if: ${{ needs.params.outputs.affects-database-schema == 'true' }}
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
|
||||
- name: Install cargo-binstall
|
||||
uses: cargo-bins/cargo-binstall@dc19f1e48450eefe5a29b8da6c6b00a87d730b37 # v1.18.1
|
||||
|
||||
- name: Install diesel_cli
|
||||
run: |
|
||||
cargo binstall diesel_cli
|
||||
|
||||
- name: Run all migrations on empty file
|
||||
run: |
|
||||
cp crates/persistence/src/schema.rs old-schema.rs
|
||||
diesel migration run --database-url="test.sqlite" --migration-dir="crates/persistence/migrations"
|
||||
|
||||
- name: Regenerate schema and check if it is consistent with the schema at HEAD
|
||||
shell: bash
|
||||
run: |
|
||||
diff -u old-schema.rs <(diesel print-schema --database-url="test.sqlite")
|
||||
|
||||
lints:
|
||||
name: Formatting + Clippy (${{ matrix.name }})
|
||||
timeout-minutes: 20
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- os: "macos"
|
||||
name: "MacOS"
|
||||
runner: ${{ fromJSON(needs.params.outputs.macos-runner) }}
|
||||
is_self_hosted: ${{ contains(fromJSON(needs.params.outputs.macos-runner), 'self-hosted') }}
|
||||
null_device: "/dev/null"
|
||||
clippy_excludes: "--exclude warp_completer"
|
||||
|
||||
- os: "linux"
|
||||
name: "Linux"
|
||||
# Use a larger (16 core, 64GB ram) runner on Linux to speed up execution time.
|
||||
# https://github.com/warpdotdev/warp-internal/settings/actions/runners
|
||||
runner: ubuntu-latest-large
|
||||
# We don't (yet) have any self-hosted Linux runners.
|
||||
is_self_hosted: false
|
||||
null_device: "/dev/null"
|
||||
clippy_excludes: "--exclude warp_completer"
|
||||
|
||||
- os: "windows"
|
||||
name: "Windows"
|
||||
runner: windows-latest-large
|
||||
# We don't (yet) have any self-hosted Windows runners.
|
||||
is_self_hosted: false
|
||||
null_device: "NUL"
|
||||
clippy_excludes: "--exclude warp_js --exclude command-signatures-v2"
|
||||
runs-on: ${{ matrix.runner }}
|
||||
needs: params
|
||||
# if: ${{ needs.params.outputs.affects-rust-sources == 'true' }}
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
|
||||
- uses: ./.github/actions/prepare_environment
|
||||
with:
|
||||
target_os: ${{ matrix.os }}
|
||||
is_self_hosted: ${{ matrix.is_self_hosted }}
|
||||
|
||||
- name: Ensure cargo.lock is up-to-date
|
||||
run:
|
||||
cargo metadata --locked --format-version=1 > ${{ matrix.null_device }} || (echo "::error::Cargo.lock is out-of-date with Cargo.toml. Run 'cargo check' to update." && exit 1)
|
||||
|
||||
- name: Run cargo fmt
|
||||
run: cargo fmt --check
|
||||
|
||||
- name: Run cargo clippy
|
||||
shell: bash
|
||||
run: |
|
||||
set -e
|
||||
# On Windows, omit --all-features because pprof and related profiling features don't compile.
|
||||
if [ "${{ matrix.os }}" = "windows" ]; then
|
||||
cargo clippy --locked --workspace ${{ matrix.clippy_excludes }} --all-targets --tests -- -D warnings
|
||||
else
|
||||
cargo clippy --locked --workspace ${{ matrix.clippy_excludes }} --all-targets --all-features --tests -- -D warnings
|
||||
# Run clippy on warp_completer with default, rather than all features enabled, because there is
|
||||
# feature-gated logic for the WIP completions-on-js implementation.
|
||||
cargo clippy --locked -p warp_completer --all-targets --tests -- -D warnings
|
||||
fi
|
||||
env:
|
||||
# Attach the GitHub auth token so that requests to the GitHub API
|
||||
# don't get rate limited during compilation (e.g.: retrieving
|
||||
# Sentry SDK binary releases).
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Run clang-format
|
||||
if: ${{ matrix.os == 'macos' }}
|
||||
run: |
|
||||
if [[ ! -d $(brew --prefix clang-format) ]]; then
|
||||
brew install clang-format
|
||||
fi
|
||||
./script/run-clang-format.py -r --extensions 'c,h,cpp,m' ./crates/warpui/src/ ./app/src/
|
||||
|
||||
general-lint:
|
||||
name: Miscellaneous checks
|
||||
timeout-minutes: 10
|
||||
strategy:
|
||||
fail-fast: false
|
||||
runs-on: ubuntu-latest
|
||||
needs: params
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
|
||||
- uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
|
||||
with:
|
||||
# Only cache runs on the master branch (caches on feature branches
|
||||
# are not reusable).
|
||||
save-if: ${{ github.ref == 'refs/heads/master' }}
|
||||
|
||||
- name: Install cargo-binstall
|
||||
uses: cargo-bins/cargo-binstall@dc19f1e48450eefe5a29b8da6c6b00a87d730b37 # v1.18.1
|
||||
|
||||
- name: Check licenses for dependencies
|
||||
run: |
|
||||
cargo binstall --force -y cargo-deny
|
||||
# Make sure all of our dependencies use licenses that are compatible with
|
||||
# our usage.
|
||||
cargo deny -L error check licenses
|
||||
|
||||
- name: Check deny.toml / about.toml license sync
|
||||
run: ./script/check_license_config_sync
|
||||
|
||||
- name: Run WGSL formatter
|
||||
run: |
|
||||
# note: this matches the version used in script/install_cargo_test_deps...
|
||||
# if changing here, probably a good idea to change there too to keep dev tools on local ~= CI
|
||||
cargo install --git https://github.com/wgsl-analyzer/wgsl-analyzer --tag "2025-06-28" wgslfmt && \
|
||||
find . -name "*.wgsl" -exec wgslfmt --check {} +
|
||||
|
||||
- name: Run PSScriptAnalyzer (PowerShell Lint)
|
||||
run: ./script/lint_powershell -ci
|
||||
shell: pwsh
|
||||
|
||||
- name: Validate repo-sync markers
|
||||
uses: warpdotdev/repo-sync/actions/validate-markers@main
|
||||
|
||||
wasm-lint:
|
||||
name: Formatting + Clippy (wasm)
|
||||
timeout-minutes: 20
|
||||
strategy:
|
||||
fail-fast: false
|
||||
runs-on: ${{ fromJSON(needs.params.outputs.wasm-runner) }}
|
||||
needs: params
|
||||
steps:
|
||||
- name: Compute IS_SELF_HOSTED
|
||||
id: is_self_hosted
|
||||
run: |
|
||||
IS_SELF_HOSTED="${{ contains(fromJSON(needs.params.outputs.wasm-runner), 'self-hosted') }}"
|
||||
echo "value=$IS_SELF_HOSTED" >> $GITHUB_OUTPUT
|
||||
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
|
||||
- uses: ./.github/actions/prepare_environment
|
||||
with:
|
||||
target_os: wasm
|
||||
is_self_hosted: ${{ steps.is_self_hosted.outputs.value }}
|
||||
|
||||
- name: Ensure cargo.lock is up-to-date
|
||||
run:
|
||||
cargo metadata --locked --format-version=1 >/dev/null || (echo "::error::Cargo.lock is out-of-date with Cargo.toml. Run 'cargo check' to update." && exit 1)
|
||||
|
||||
- name: Run cargo fmt
|
||||
run: cargo fmt --check
|
||||
|
||||
- name: Run cargo clippy
|
||||
run: |
|
||||
cargo clippy --locked --target wasm32-unknown-unknown --profile release-wasm-debug_assertions -- -D warnings
|
||||
|
||||
check-release-compilation:
|
||||
name: Verify compilation with release flags (${{ matrix.name }})
|
||||
timeout-minutes: 15
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- os: "macos"
|
||||
name: "MacOS"
|
||||
runner: ${{ fromJSON(needs.params.outputs.macos-runner) }}
|
||||
is_self_hosted: ${{ contains(fromJSON(needs.params.outputs.macos-runner), 'self-hosted') }}
|
||||
null_device: "/dev/null"
|
||||
|
||||
- os: "linux"
|
||||
name: "Linux"
|
||||
# Use a larger (16 core, 64GB ram) runner on Linux to speed up execution time.
|
||||
# https://github.com/warpdotdev/warp-internal/settings/actions/runners
|
||||
runner: ubuntu-latest-large
|
||||
# We don't (yet) have any self-hosted Linux runners.
|
||||
is_self_hosted: false
|
||||
null_device: "/dev/null"
|
||||
|
||||
- os: "windows"
|
||||
name: "Windows"
|
||||
runner: windows-latest-large
|
||||
# We don't (yet) have any self-hosted Windows runners.
|
||||
is_self_hosted: false
|
||||
null_device: "NUL"
|
||||
|
||||
- os: "wasm"
|
||||
name: "wasm"
|
||||
runner: ${{ fromJSON(needs.params.outputs.wasm-runner) }}
|
||||
is_self_hosted: ${{ contains(fromJSON(needs.params.outputs.wasm-runner), 'self-hosted') }}
|
||||
null_device: "/dev/null"
|
||||
runs-on: ${{ matrix.runner }}
|
||||
needs: params
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
|
||||
- uses: ./.github/actions/prepare_environment
|
||||
with:
|
||||
target_os: ${{ matrix.os }}
|
||||
is_self_hosted: ${{ matrix.is_self_hosted }}
|
||||
|
||||
- name: Verify compilation with release flags
|
||||
shell: bash
|
||||
run: |
|
||||
if ${{ matrix.os == 'wasm' }}; then
|
||||
./script/wasm/bundle --bin dev --nouniversal --check-only
|
||||
else
|
||||
./script/bundle --bin dev --nouniversal --check-only
|
||||
fi
|
||||
env:
|
||||
# Attach the GitHub auth token so that requests to the GitHub API
|
||||
# don't get rate limited during compilation (e.g.: retrieving
|
||||
# Sentry SDK binary releases).
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
# A final job that collects the results of all of the parallel CI jobs and
|
||||
# makes sure that all of them pass.
|
||||
#
|
||||
# This allows us to tell GitHub that the only required check is this "gather"
|
||||
# job, simplifying the process of adding and removing jobs to the CI
|
||||
# workflow.
|
||||
ci-result:
|
||||
name: Check CI results
|
||||
timeout-minutes: 3
|
||||
runs-on: ubuntu-latest
|
||||
# Always run this job, as it needs to check the results of all of the other
|
||||
# jobs. If we skipped CI checks (e.g. on a draft PR), also skip this one.
|
||||
if: ${{ !cancelled() && needs.params.result != 'skipped' }}
|
||||
needs:
|
||||
- params
|
||||
- database-migration
|
||||
- tests
|
||||
- lints
|
||||
- wasm-lint
|
||||
- general-lint
|
||||
- check-release-compilation
|
||||
steps:
|
||||
- name: Check CI results
|
||||
run: |
|
||||
# Convert needs context to JSON and check required jobs
|
||||
echo '${{ toJSON(needs) }}' | jq -r '
|
||||
# Get all jobs except database-migration
|
||||
to_entries |
|
||||
map(select(.key != "database-migration")) |
|
||||
.[] |
|
||||
if .value.result != "success" then
|
||||
"::error::Required job \(.key) failed or was skipped (status: \(.value.result))"
|
||||
else
|
||||
empty
|
||||
end
|
||||
' | {
|
||||
if read -r error; then
|
||||
echo "$error"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Special handling for conditional database-migration job.
|
||||
affects_schema="${{ needs.params.outputs.affects-database-schema }}"
|
||||
migration_result="${{ needs.database-migration.result }}"
|
||||
|
||||
if [[ "$affects_schema" == "true" && "$migration_result" != "success" ]]; then
|
||||
echo "::error::Database migration job failed or was skipped when it should have run (status: $migration_result)"
|
||||
exit 1
|
||||
elif [[ "$affects_schema" != "true" && "$migration_result" != "skipped" ]]; then
|
||||
echo "::error::Database migration job should have been skipped but wasn't (status: $migration_result)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "All jobs completed successfully!"
|
||||
@@ -0,0 +1,75 @@
|
||||
|
||||
# ======================================================================================
|
||||
# Workflow: Close Stale Fix PRs
|
||||
# ======================================================================================
|
||||
# Usage:
|
||||
# - This workflow runs daily and closes agent-opened CI fix PRs that have been
|
||||
# open for more than 3 days.
|
||||
#
|
||||
# Expected Output:
|
||||
# - Any open PR whose branch matches the oz-agent-fix/run-* convention and that
|
||||
# was opened more than 3 days ago will be closed with an explanatory comment.
|
||||
#
|
||||
# ======================================================================================
|
||||
|
||||
name: Close Stale Fix PRs
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 12 * * *' # 8am EST
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
close-stale-fix-prs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Close Stale Fix PRs
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
||||
with:
|
||||
script: |
|
||||
const { owner, repo } = context.repo;
|
||||
const staleCutoffMs = 3 * 24 * 60 * 60 * 1000; // 3 days in milliseconds
|
||||
const now = Date.now();
|
||||
|
||||
// Paginate through all open PRs to find ones matching the branch convention
|
||||
const openPRs = await github.paginate(github.rest.pulls.list, {
|
||||
owner,
|
||||
repo,
|
||||
state: 'open',
|
||||
per_page: 100,
|
||||
});
|
||||
|
||||
const staleFixPRs = openPRs.filter(pr => {
|
||||
const branchMatches = /^oz-agent-fix\/run-/.test(pr.head.ref);
|
||||
const openedAt = new Date(pr.created_at).getTime();
|
||||
const isStale = (now - openedAt) > staleCutoffMs;
|
||||
return branchMatches && isStale;
|
||||
});
|
||||
|
||||
if (staleFixPRs.length === 0) {
|
||||
console.log('No stale fix PRs found.');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`Found ${staleFixPRs.length} stale fix PR(s). Closing them...`);
|
||||
|
||||
for (const pr of staleFixPRs) {
|
||||
const ageMs = now - new Date(pr.created_at).getTime();
|
||||
const ageDays = Math.floor(ageMs / (24 * 60 * 60 * 1000));
|
||||
console.log(`Closing PR #${pr.number} (branch: ${pr.head.ref}, open for ${ageDays} day(s)): ${pr.title}`);
|
||||
|
||||
await github.rest.issues.createComment({
|
||||
owner,
|
||||
repo,
|
||||
issue_number: pr.number,
|
||||
body: `Closing this automated CI fix PR because it has been open for more than 3 days (${ageDays} day(s)) without being merged. Please review the underlying CI failures manually if they are still relevant.`,
|
||||
});
|
||||
|
||||
await github.rest.pulls.update({
|
||||
owner,
|
||||
repo,
|
||||
pull_number: pr.number,
|
||||
state: 'closed',
|
||||
});
|
||||
}
|
||||
|
||||
console.log('Done.');
|
||||
@@ -0,0 +1,16 @@
|
||||
name: Comment on Unready Assigned Issue (Local)
|
||||
on:
|
||||
issues:
|
||||
types: [assigned]
|
||||
concurrency:
|
||||
group: comment-on-unready-assigned-issue-${{ github.event.issue.number || github.run_id }}
|
||||
cancel-in-progress: false
|
||||
jobs:
|
||||
comment_when_unready:
|
||||
if: github.event.assignee.login == 'oz-agent' && !contains(github.event.issue.labels.*.name, 'ready-to-spec') && !contains(github.event.issue.labels.*.name, 'ready-to-implement')
|
||||
permissions:
|
||||
issues: write
|
||||
uses: warpdotdev/oz-for-oss/.github/workflows/comment-on-unready-assigned-issue.yml@main
|
||||
secrets:
|
||||
OZ_MGMT_GHA_APP_ID: ${{ secrets.OZ_MGMT_GHA_APP_ID }}
|
||||
OZ_MGMT_GHA_PRIVATE_KEY: ${{ secrets.OZ_MGMT_GHA_PRIVATE_KEY }}
|
||||
@@ -0,0 +1,34 @@
|
||||
name: Create Implementation from Issue (Local)
|
||||
on:
|
||||
issues:
|
||||
types: [assigned, labeled]
|
||||
issue_comment:
|
||||
types: [created]
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
issue_number:
|
||||
description: Issue number to create an implementation for
|
||||
required: true
|
||||
type: string
|
||||
concurrency:
|
||||
group: create-implementation-issue-${{ github.event.issue.number || inputs.issue_number || github.run_id }}
|
||||
cancel-in-progress: false
|
||||
jobs:
|
||||
# Mention, bot, event-type, and trust gates all live in the reusable
|
||||
# workflow (``create-implementation-from-issue.yml``). This adapter
|
||||
# exists only to subscribe to the GitHub events that can trigger
|
||||
# implementation work (``issues`` assign/label by a maintainer, or a
|
||||
# trusted ``@oz-agent`` issue comment) and delegate them through
|
||||
# ``workflow_call``.
|
||||
create_implementation:
|
||||
permissions:
|
||||
contents: write
|
||||
issues: write
|
||||
pull-requests: write
|
||||
uses: warpdotdev/oz-for-oss/.github/workflows/create-implementation-from-issue.yml@main
|
||||
with:
|
||||
issue_number: ${{ github.event.inputs.issue_number || '' }}
|
||||
secrets:
|
||||
OZ_MGMT_GHA_APP_ID: ${{ secrets.OZ_MGMT_GHA_APP_ID }}
|
||||
OZ_MGMT_GHA_PRIVATE_KEY: ${{ secrets.OZ_MGMT_GHA_PRIVATE_KEY }}
|
||||
WARP_API_KEY: ${{ secrets.OSS_WARP_API_KEY }}
|
||||
@@ -0,0 +1,34 @@
|
||||
name: Create Spec from Issue (Local)
|
||||
on:
|
||||
issues:
|
||||
types: [assigned, labeled]
|
||||
issue_comment:
|
||||
types: [created]
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
issue_number:
|
||||
description: Issue number to create a spec for
|
||||
required: true
|
||||
type: string
|
||||
concurrency:
|
||||
group: create-spec-issue-${{ github.event.issue.number || inputs.issue_number || github.run_id }}
|
||||
cancel-in-progress: false
|
||||
jobs:
|
||||
# Mention, bot, event-type, and trust gates all live in the reusable
|
||||
# workflow (``create-spec-from-issue.yml``). This adapter exists only
|
||||
# to subscribe to the GitHub events that can trigger spec creation
|
||||
# (``issues`` assign/label by a maintainer, or a trusted
|
||||
# ``@oz-agent`` issue comment) and delegate them through
|
||||
# ``workflow_call``.
|
||||
create_spec:
|
||||
permissions:
|
||||
contents: write
|
||||
issues: write
|
||||
pull-requests: write
|
||||
uses: warpdotdev/oz-for-oss/.github/workflows/create-spec-from-issue.yml@main
|
||||
with:
|
||||
issue_number: ${{ github.event.inputs.issue_number || '' }}
|
||||
secrets:
|
||||
OZ_MGMT_GHA_APP_ID: ${{ secrets.OZ_MGMT_GHA_APP_ID }}
|
||||
OZ_MGMT_GHA_PRIVATE_KEY: ${{ secrets.OZ_MGMT_GHA_PRIVATE_KEY }}
|
||||
WARP_API_KEY: ${{ secrets.OSS_WARP_API_KEY }}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,54 @@
|
||||
# A workflow to cut a new release candidate (i.e.: .stable_01) on the current
|
||||
# release branch.
|
||||
#
|
||||
# The channel will be automatically determined from the branch name (i.e.: when
|
||||
# run on a stable_release/* branch, it will assume it is building a new "stable"
|
||||
# channel candidate).
|
||||
|
||||
name: Cut New Release Candidate
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
compute_channel:
|
||||
name: Compute release channel
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
channel: ${{ steps.compute-channel.outputs.channel }}
|
||||
steps:
|
||||
- name: "Compute release channel"
|
||||
id: compute-channel
|
||||
run: |
|
||||
# Check whether the current branch name starts with "some_release/",
|
||||
# and if so, extract the value of "some" into the CHANNEL variable,
|
||||
# discarding the rest of the branch name.
|
||||
CHANNEL="$(echo $GITHUB_REF_NAME | sed -r 's|^(\w+)_release/.*$|\1|')"
|
||||
# If the sed expression doesn't match the input, it will write out the
|
||||
# input, unmodified.
|
||||
if [[ $CHANNEL == $GITHUB_REF_NAME ]]; then
|
||||
echo "::error::Can only create new release candidates on a release branch" && exit 1
|
||||
fi
|
||||
echo "channel=$CHANNEL" >> $GITHUB_OUTPUT
|
||||
shell: bash
|
||||
|
||||
create_release_candidate:
|
||||
name: Create Release Candidate
|
||||
uses: ./.github/workflows/create_release.yml
|
||||
needs: compute_channel
|
||||
with:
|
||||
channel: ${{ needs.compute_channel.outputs.channel }}
|
||||
secrets: inherit
|
||||
|
||||
update_channel_versions:
|
||||
name: Update channel_versions.json
|
||||
needs:
|
||||
- create_release_candidate
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Invoke update workflow in channel-versions repository
|
||||
uses: peter-evans/repository-dispatch@28959ce8df70de7be546dd1250a005dd32156697 # v4.0.1
|
||||
with:
|
||||
repository: warpdotdev/channel-versions
|
||||
token: ${{ secrets.CHANNEL_VERSIONS_REPOSITORY_DISPATCH_TOKEN }}
|
||||
event-type: update-channel-versions
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
# A workflow to cut new releases (i.e.: .stable_00) for various release
|
||||
# channels.
|
||||
#
|
||||
# Supports cutting releases for nightly release channels, weekly release
|
||||
# channels, or all release channels. Releases run in parallel where able.
|
||||
|
||||
name: Cut New Releases
|
||||
on:
|
||||
schedule:
|
||||
# Run this job every morning at 3am EST.
|
||||
- cron: "0 8 * * *"
|
||||
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
targets:
|
||||
type: choice
|
||||
description: Which releases to build. Nightly = dev; Weekly = preview/stable.
|
||||
options:
|
||||
- nightly
|
||||
- weekly
|
||||
- all
|
||||
|
||||
env:
|
||||
CONFIG_FILE: ".github/workflows/release_configurations.json"
|
||||
# The day of the week when the weekly release should be cut. This is a value
|
||||
# from 1-7, with 1 representing Monday, 2 representing Tuesday, etc.
|
||||
WEEKLY_RELEASE_DAY: 3 # Cut weekly builds on Wednesdays.
|
||||
|
||||
jobs:
|
||||
get_config:
|
||||
name: Get release configuration
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
SCHEDULE: ${{ github.event.schedule }}
|
||||
TARGETS: ${{ inputs.targets }}
|
||||
outputs:
|
||||
channels: ${{ steps.get-config.outputs.channels }}
|
||||
steps:
|
||||
- name: Check for master branch
|
||||
run: |
|
||||
[ $GITHUB_REF == "refs/heads/master" ] || (echo "::error::Can only cut new releases on the master branch" && exit 1)
|
||||
shell: bash
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- id: get-config
|
||||
run: |
|
||||
# Check to see if this was auto-run as a cron job. If so, set the
|
||||
# value of TARGETS based on whether the current day of the week
|
||||
# is when we build weekly releases (in addition to nightly ones).
|
||||
if [[ ! -z "$SCHEDULE" ]]; then
|
||||
if [[ "$(date +%u)" == "$WEEKLY_RELEASE_DAY" ]]; then
|
||||
TARGETS="all"
|
||||
else
|
||||
TARGETS="nightly"
|
||||
fi
|
||||
fi
|
||||
|
||||
echo ::echo::on
|
||||
if [[ "$TARGETS" == "all" ]]; then
|
||||
TARGETS="$(jq -rc '[.channels[] | .type] | unique' $CONFIG_FILE)"
|
||||
else
|
||||
TARGETS="[\"$TARGETS\"]"
|
||||
fi
|
||||
export CHANNELS="$(jq -rc '[.channels[] | select(.type | inside($ENV.TARGETS)) | .channel]' $CONFIG_FILE)"
|
||||
echo "channels=$CHANNELS" >> $GITHUB_OUTPUT
|
||||
# CHANNELS_INCLUDE_AUTOPUSH=$(jq -rc '[.channels[] | select(.channel | inside($ENV.CHANNELS)) | .is_autopush] | any' $CONFIG_FILE)
|
||||
# echo "channels-include-autopush=$CHANNELS_INCLUDE_AUTOPUSH" >> $GITHUB_OUTPUT
|
||||
echo ::echo::off
|
||||
shell: bash
|
||||
|
||||
create_release:
|
||||
name: Create Release
|
||||
uses: ./.github/workflows/create_release.yml
|
||||
needs: get_config
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
channel: ${{ fromJSON(needs.get_config.outputs.channels) }}
|
||||
with:
|
||||
channel: ${{ matrix.channel }}
|
||||
secrets: inherit
|
||||
|
||||
update_channel_versions:
|
||||
name: Update channel_versions.json
|
||||
needs:
|
||||
- create_release
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Invoke update workflow in channel-versions repository
|
||||
uses: peter-evans/repository-dispatch@28959ce8df70de7be546dd1250a005dd32156697 # v4.0.1
|
||||
with:
|
||||
repository: warpdotdev/channel-versions
|
||||
token: ${{ secrets.CHANNEL_VERSIONS_REPOSITORY_DISPATCH_TOKEN }}
|
||||
event-type: update-channel-versions
|
||||
@@ -0,0 +1,111 @@
|
||||
# A workflow to delete (soft-delete) a release branch by renaming it.
|
||||
#
|
||||
# The branch will be renamed from its original name to deleted/{original_name}.
|
||||
# For example, stable_release/v0.2025.01.01.00.00 becomes
|
||||
# deleted/stable_release/v0.2025.01.01.00.00.
|
||||
#
|
||||
# This workflow will fail if:
|
||||
# - The branch doesn't start with a release channel prefix
|
||||
# (i.e.: stable_release/, preview_release/, or dev_release/)
|
||||
# - The release has an entry in channel_versions.json (i.e., it's deployed)
|
||||
|
||||
name: Delete Release
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
branch:
|
||||
description: 'The release branch to delete (e.g., stable_release/v0.2025.01.01.00.00.stable)'
|
||||
required: true
|
||||
type: string
|
||||
confirmation:
|
||||
description: 'Type DELETE to confirm'
|
||||
required: true
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
delete_release:
|
||||
name: Delete Release Branch
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Verify confirmation
|
||||
run: |
|
||||
if [[ "${{ inputs.confirmation }}" != "DELETE" ]]; then
|
||||
echo "::error::You must type DELETE to confirm branch deletion. \
|
||||
If you're unsure about deleting, ask a TL in #oncall-client before running this workflow."
|
||||
exit 1
|
||||
fi
|
||||
shell: bash
|
||||
|
||||
- name: Validate release branch
|
||||
id: validate
|
||||
run: |
|
||||
BRANCH_NAME="${{ inputs.branch }}"
|
||||
|
||||
# Validate branch is a release branch and extract the channel
|
||||
if [[ ! "$BRANCH_NAME" =~ ^(stable|preview|dev)_release/ ]]; then
|
||||
echo "::error::Branch '$BRANCH_NAME' is not a release branch." && exit 1
|
||||
fi
|
||||
CHANNEL="${BASH_REMATCH[1]}"
|
||||
|
||||
# Extract the version base from the branch name
|
||||
# e.g., "stable_release/v0.2025.01.26.12.30.stable" -> "v0.2025.01.26.12.30.stable"
|
||||
VERSION_BASE="$(echo $BRANCH_NAME | sed -r 's|^\w+_release/(.*)$|\1|')"
|
||||
|
||||
echo "branch_name=$BRANCH_NAME" >> $GITHUB_OUTPUT
|
||||
echo "new_branch_name=deleted/$BRANCH_NAME" >> $GITHUB_OUTPUT
|
||||
echo "channel=$CHANNEL" >> $GITHUB_OUTPUT
|
||||
echo "version_base=$VERSION_BASE" >> $GITHUB_OUTPUT
|
||||
shell: bash
|
||||
|
||||
- name: Check if release has changelog in channel_versions.json
|
||||
id: check_deployed
|
||||
env:
|
||||
CHANNEL: ${{ steps.validate.outputs.channel }}
|
||||
VERSION_BASE: ${{ steps.validate.outputs.version_base }}
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
# Fetch channel_versions.json from the channel-versions repository
|
||||
CHANNEL_VERSIONS=$(gh api repos/warpdotdev/channel-versions/contents/channel_versions.json --jq '.content' | base64 -d)
|
||||
|
||||
# Fail fast if the channel section is missing from channel_versions.json
|
||||
CHANNEL_EXISTS=$(echo "$CHANNEL_VERSIONS" | jq -r --arg channel "$CHANNEL" 'has("changelogs") and (.changelogs | has($channel))')
|
||||
if [[ "$CHANNEL_EXISTS" != "true" ]]; then
|
||||
echo "::error::channel_versions.json is missing the 'changelogs.$CHANNEL' section. Cannot safely determine if release is deployed."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check if any key in the changelogs section for this channel starts with the version base
|
||||
# Keys are like "v0.2025.01.26.12.30.stable_01" and version_base is "v0.2025.01.26.12.30.stable"
|
||||
MATCHING_ENTRIES=$(echo "$CHANNEL_VERSIONS" | jq -r --arg channel "$CHANNEL" --arg version "$VERSION_BASE" \
|
||||
'.changelogs[$channel] | keys[] | select(startswith($version))')
|
||||
|
||||
if [[ -n "$MATCHING_ENTRIES" ]]; then
|
||||
echo "::error::Cannot delete release branch: version '$VERSION_BASE' has changelog entries in channel_versions.json:"
|
||||
echo "$MATCHING_ENTRIES"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "::notice::Release has no changelog in channel_versions.json, safe to delete."
|
||||
shell: bash
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
ref: ${{ inputs.branch }}
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Rename branch to deleted/
|
||||
env:
|
||||
BRANCH_NAME: ${{ steps.validate.outputs.branch_name }}
|
||||
NEW_BRANCH_NAME: ${{ steps.validate.outputs.new_branch_name }}
|
||||
run: |
|
||||
echo "Renaming branch '$BRANCH_NAME' to '$NEW_BRANCH_NAME'"
|
||||
|
||||
# Push the current HEAD to the new branch name
|
||||
git push origin "HEAD:refs/heads/$NEW_BRANCH_NAME"
|
||||
|
||||
# Delete the original branch
|
||||
git push origin --delete "$BRANCH_NAME"
|
||||
|
||||
echo "::notice::Successfully renamed branch '$BRANCH_NAME' to '$NEW_BRANCH_NAME'"
|
||||
shell: bash
|
||||
@@ -0,0 +1,69 @@
|
||||
name: "Run Docubot on @docubot mention"
|
||||
on:
|
||||
# These triggers are currently disabled because they cause workflow runs for ALL
|
||||
# comments in the org, even though the job only executes when @docubot is mentioned,
|
||||
# which is noisy.
|
||||
# issue_comment:
|
||||
# types: [created]
|
||||
# pull_request_review_comment:
|
||||
# types: [created]
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
pr_number:
|
||||
description: 'PR number to run Docubot on (leave empty to run on current branch)'
|
||||
required: false
|
||||
type: string
|
||||
prompt:
|
||||
description: Prompt to send to Docubot
|
||||
required: true
|
||||
default: "Analyze this PR for documentation needs"
|
||||
warp_channel:
|
||||
type: choice
|
||||
description: Warp release channel
|
||||
required: true
|
||||
default: dev
|
||||
options:
|
||||
- dev
|
||||
- preview
|
||||
- stable
|
||||
profile_id:
|
||||
description: Warp profile ID
|
||||
required: false
|
||||
permissions:
|
||||
contents: write
|
||||
issues: write
|
||||
pull-requests: write
|
||||
id-token: write
|
||||
actions: read
|
||||
|
||||
jobs:
|
||||
run_docubot:
|
||||
if: >
|
||||
(
|
||||
contains(github.event.comment.body, '@docubot') || inputs.prompt
|
||||
)
|
||||
&& github.actor != 'github-actions[bot]'
|
||||
runs-on: namespace-profile-ubuntu-20-04
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
with:
|
||||
# Check out the appropriate branch based on trigger type
|
||||
# TODO: do this in sdk?
|
||||
ref: >
|
||||
${{
|
||||
github.event_name == 'workflow_dispatch'
|
||||
&& (inputs.pr_number != '' && format('refs/pull/{0}/head', inputs.pr_number) || github.ref)
|
||||
|| (
|
||||
github.event_name == 'pull_request_review_comment'
|
||||
&& format('refs/pull/{0}/head', github.event.pull_request.number)
|
||||
|| format('refs/pull/{0}/head', github.event.issue.number)
|
||||
)
|
||||
}}
|
||||
- name: Call Docubot
|
||||
uses: ./.github/actions/docubot
|
||||
with:
|
||||
prompt: ${{ github.event.comment.body || inputs.prompt}}
|
||||
warp_api_key: ${{ secrets.WARP_DEV_API_KEY }}
|
||||
warp_channel: ${{ inputs.warp_channel || 'dev' }}
|
||||
profile_id: ${{ inputs.profile_id || vars.WARP_DEV_API_PROFILE_ID }}
|
||||
github_token: ${{ secrets.PEI_GH_TOKEN_PLS_DEL }}
|
||||
@@ -0,0 +1,39 @@
|
||||
name: Enforce PR Issue State Logic
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
pr_number:
|
||||
description: Pull request number to evaluate
|
||||
required: true
|
||||
type: string
|
||||
requester:
|
||||
description: Login of the user whose action triggered enforcement, if any
|
||||
required: false
|
||||
default: ""
|
||||
type: string
|
||||
secrets:
|
||||
OZ_MGMT_GHA_APP_ID:
|
||||
required: true
|
||||
OZ_MGMT_GHA_PRIVATE_KEY:
|
||||
required: true
|
||||
OSS_WARP_API_KEY:
|
||||
required: true
|
||||
outputs:
|
||||
allow_review:
|
||||
description: Whether downstream PR hooks may continue after enforcement.
|
||||
value: ${{ jobs.enforce_issue_state.outputs.allow_review }}
|
||||
jobs:
|
||||
enforce_issue_state:
|
||||
name: Enforce PR issue state
|
||||
permissions:
|
||||
contents: read
|
||||
issues: write
|
||||
pull-requests: write
|
||||
uses: warpdotdev/oz-for-oss/.github/workflows/enforce-pr-issue-state.yml@main
|
||||
with:
|
||||
pr_number: ${{ inputs.pr_number }}
|
||||
requester: ${{ inputs.requester }}
|
||||
secrets:
|
||||
OZ_MGMT_GHA_APP_ID: ${{ secrets.OZ_MGMT_GHA_APP_ID }}
|
||||
OZ_MGMT_GHA_PRIVATE_KEY: ${{ secrets.OZ_MGMT_GHA_PRIVATE_KEY }}
|
||||
WARP_API_KEY: ${{ secrets.OSS_WARP_API_KEY }}
|
||||
@@ -0,0 +1,261 @@
|
||||
name: Feature Flag Cleanup
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 15 * * *' # 10am EST
|
||||
workflow_dispatch:
|
||||
|
||||
# Default to a read-only token. Jobs that need to push or open PRs widen
|
||||
# permissions explicitly below.
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze feature flags
|
||||
runs-on: namespace-profile-ubuntu-small
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: read
|
||||
outputs:
|
||||
flag_name: ${{ steps.select_flag.outputs.flag_name }}
|
||||
cargo_flag: ${{ steps.select_flag.outputs.cargo_flag }}
|
||||
reviewers: ${{ steps.select_flag.outputs.reviewers }}
|
||||
analyzed_sha: ${{ steps.record_sha.outputs.analyzed_sha }}
|
||||
artifact_url: ${{ steps.upload_feature_flag_log.outputs.artifact-url }}
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: namespacelabs/nscloud-checkout-action@938f5d2d403d6224d9a0c0dc559b1dae09c2ede4 # v8.1.1
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Record analyzed commit
|
||||
id: record_sha
|
||||
run: |
|
||||
echo "analyzed_sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Find old feature flags
|
||||
uses: warpdotdev/oz-agent-action@main
|
||||
with:
|
||||
prompt: |
|
||||
Your task is to find and list any feature flags which have been enabled by default for more than 3 months.
|
||||
|
||||
There will always be at least one such flag.
|
||||
|
||||
## Context
|
||||
Feature flags are defined in warp_core/src/features.rs. They're enabled by default in app/Cargo.toml, using the default feature list. The mapping between Cargo features and app features is in the enabled_features function of app/src/lib.rs.
|
||||
|
||||
## Recommended Procedure
|
||||
1. Use `git blame` on the Cargo.toml file to identify when each feature flag was enabled by default.
|
||||
2. Read the enabled_features function to map Cargo features to app features.
|
||||
3. Write features that have been enabled by default for more than 3 months to the output file.
|
||||
|
||||
You may create temporary files to store useful results, and write helper scripts for subtasks. It is not necessary to do everything in one shot.
|
||||
|
||||
## Result Format
|
||||
List the old feature flags in ${{ runner.temp }}/flags_to_cleanup.jsonl, in this format (one flag per line):
|
||||
{ "flag_name": "MyFlag", "cargo_flag": "my_flag", "date_enabled": "2025-02-21", "enabling_commit": "aad3293b8fe" }
|
||||
|
||||
## Reminders
|
||||
* Fields in the output JSONL file have these meanings:
|
||||
* "flag_name" - the name of the FeatureFlag enum variant
|
||||
* "cargo_flag" - the name of the flag used in Cargo.toml
|
||||
* "date_enabled" - the date on which the feature flag was enabled by default
|
||||
* "enabling_commit" - the Git commit in which the feature flag was enabled by default
|
||||
* You are running as part of a GitHub automation and must not commit or push any changes. Another agent will process the file you produce.
|
||||
warp_api_key: ${{ secrets.WARP_API_KEY }}
|
||||
share: team
|
||||
|
||||
- name: Upload feature flag log
|
||||
id: upload_feature_flag_log
|
||||
uses: namespace-actions/upload-artifact@f6ccaacc655aec41b93af180d1d7eef21af862d2 # v1.0.3
|
||||
with:
|
||||
name: flags-to-cleanup
|
||||
path: ${{ runner.temp }}/flags_to_cleanup.jsonl
|
||||
|
||||
# As we test out the workflow, only clean up one flag at a time.
|
||||
# We could also pick several flags, or fan out jobs for every single one.
|
||||
- name: Select a flag to clean up
|
||||
id: select_flag
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
cleanup_log="$RUNNER_TEMP/flags_to_cleanup.jsonl"
|
||||
# Skip over flags that are also used to enable/disable functionality at runtime.
|
||||
excluded_flags='["CreatingSharedSessions", "ViewingSharedSessions", "AgentMode"]'
|
||||
|
||||
flag_name=""
|
||||
cargo_flag=""
|
||||
|
||||
# Iterate flags in order (oldest first) and pick the first one that is not
|
||||
# excluded and does not already have an open cleanup PR. Sort by
|
||||
# date_enabled here rather than relying on the analysis agent to emit
|
||||
# the JSONL in any particular order.
|
||||
while IFS= read -r candidate; do
|
||||
[ -z "$candidate" ] && continue
|
||||
candidate_flag="$(jq -r '.flag_name' <<< "$candidate")"
|
||||
|
||||
if [ "$(jq -n --arg flag "$candidate_flag" --argjson excluded "$excluded_flags" '$excluded | index($flag) != null')" = "true" ]; then
|
||||
echo "Skipping $candidate_flag: in excluded list"
|
||||
continue
|
||||
fi
|
||||
|
||||
candidate_branch="oz-agent/cleanup-feature-flag-$candidate_flag"
|
||||
existing_pr="$(gh pr list --state open --head "$candidate_branch" --json number --jq '.[0].number')"
|
||||
if [ -n "$existing_pr" ] && [ "$existing_pr" != "null" ]; then
|
||||
echo "Skipping $candidate_flag: open cleanup PR #$existing_pr already exists on $candidate_branch"
|
||||
continue
|
||||
fi
|
||||
|
||||
flag_name="$candidate_flag"
|
||||
cargo_flag="$(jq -r '.cargo_flag' <<< "$candidate")"
|
||||
break
|
||||
done < <(jq -c -s 'sort_by(.date_enabled) | .[]' "$cleanup_log")
|
||||
echo "flag_name=$flag_name" >> "$GITHUB_OUTPUT"
|
||||
echo "cargo_flag=$cargo_flag" >> "$GITHUB_OUTPUT"
|
||||
|
||||
echo "Cleaning up $flag_name ($cargo_flag)"
|
||||
|
||||
if [ -z "$flag_name" ] || [ "$flag_name" = "null" ]; then
|
||||
echo "No flag selected for cleanup"
|
||||
echo "reviewers=" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
enabling_commit="$(jq -r -cs --arg flag "$flag_name" '.[] | select(.flag_name == $flag) | .enabling_commit' "$cleanup_log")"
|
||||
committer_username=""
|
||||
|
||||
# Resolve the GitHub username of the commit author directly via the
|
||||
# commits API. This is more reliable than mapping a commit email to
|
||||
# a GitHub user, since commit emails are often noreply addresses or
|
||||
# otherwise unsearchable.
|
||||
if [ -n "$enabling_commit" ] && [ "$enabling_commit" != "null" ]; then
|
||||
committer_username="$(gh api "repos/${{ github.repository }}/commits/$enabling_commit" --jq '.author.login // ""' 2>/dev/null || echo "")"
|
||||
fi
|
||||
|
||||
default_reviewers="bennavetta"
|
||||
|
||||
# Verify the committer is still a collaborator before assigning as reviewer.
|
||||
if [ -n "$committer_username" ] && gh api "repos/${{ github.repository }}/collaborators/$committer_username" --verbose 2>/dev/null; then
|
||||
echo "Flag originally enabled by $committer_username"
|
||||
echo "reviewers=$committer_username,$default_reviewers" >> "$GITHUB_OUTPUT"
|
||||
elif [ -n "$committer_username" ]; then
|
||||
echo "$committer_username is no longer a collaborator, using fallback"
|
||||
echo "reviewers=$default_reviewers" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "Could not determine flag committer, using fallback"
|
||||
echo "reviewers=$default_reviewers" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
fi
|
||||
|
||||
cleanup:
|
||||
name: Run cleanup agent
|
||||
needs: analyze
|
||||
if: ${{ needs.analyze.outputs.flag_name && needs.analyze.outputs.flag_name != 'null' }}
|
||||
runs-on: namespace-profile-ubuntu-small
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: namespacelabs/nscloud-checkout-action@938f5d2d403d6224d9a0c0dc559b1dae09c2ede4 # v8.1.1
|
||||
with:
|
||||
ref: ${{ needs.analyze.outputs.analyzed_sha }}
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup Rust cache
|
||||
uses: namespacelabs/nscloud-cache-action@a90bb5d4b27522ce881c6e98eebd7d7e6d1653f9 # v1.4.2
|
||||
with:
|
||||
cache: rust
|
||||
|
||||
- name: Clean up the unused feature flag
|
||||
id: cleanup_agent
|
||||
uses: warpdotdev/oz-agent-action@main
|
||||
with:
|
||||
prompt: |
|
||||
The feature flag ${{ needs.analyze.outputs.flag_name }} is now enabled by default.
|
||||
|
||||
Clean up all references to this flag, modifying code to always assume it is enabled. Ensure that the code is correctly formatted and that tests still pass.
|
||||
|
||||
Finally, remove the flag from the `FeatureFlag` enum, and remove the equivalent ${{ needs.analyze.outputs.cargo_flag }} feature in app/Cargo.toml
|
||||
|
||||
You are running as part of a GitHub automation that runs with a read-only token and will package your changes into a patch for a separate job to commit. Do not create a branch, do not commit, do not push, do not create a PR, and do not call `gh`. Leave your changes in the working tree only.
|
||||
share: team
|
||||
warp_api_key: ${{ secrets.WARP_API_KEY }}
|
||||
|
||||
- name: Generate cleanup patch
|
||||
env:
|
||||
ANALYZED_SHA: ${{ needs.analyze.outputs.analyzed_sha }}
|
||||
run: |
|
||||
# If the cleanup agent made any local commits, undo them while keeping
|
||||
# the changes in the working tree, so the resulting patch reflects the
|
||||
# full set of changes relative to the analyzed commit.
|
||||
git reset --mixed "$ANALYZED_SHA"
|
||||
# Stage all changes (modified, new, deleted) so the patch is complete.
|
||||
git add -A
|
||||
git diff --staged --binary > "$RUNNER_TEMP/cleanup.patch"
|
||||
if [ ! -s "$RUNNER_TEMP/cleanup.patch" ]; then
|
||||
echo "Cleanup agent produced no changes" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Upload cleanup patch
|
||||
uses: namespace-actions/upload-artifact@f6ccaacc655aec41b93af180d1d7eef21af862d2 # v1.0.3
|
||||
with:
|
||||
name: cleanup-patch
|
||||
path: ${{ runner.temp }}/cleanup.patch
|
||||
|
||||
create_pr:
|
||||
name: Create cleanup PR
|
||||
needs: [analyze, cleanup]
|
||||
if: ${{ needs.analyze.outputs.flag_name && needs.analyze.outputs.flag_name != 'null' }}
|
||||
runs-on: namespace-profile-ubuntu-small
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: namespacelabs/nscloud-checkout-action@938f5d2d403d6224d9a0c0dc559b1dae09c2ede4 # v8.1.1
|
||||
with:
|
||||
ref: ${{ needs.analyze.outputs.analyzed_sha }}
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Download cleanup patch
|
||||
uses: namespace-actions/download-artifact@7cbad919e4b0e09f17e9d6311a444ff002992b5b # v2.0.1
|
||||
with:
|
||||
name: cleanup-patch
|
||||
path: ${{ runner.temp }}
|
||||
|
||||
- name: Apply cleanup patch
|
||||
run: |
|
||||
git apply --binary --whitespace=nowarn "$RUNNER_TEMP/cleanup.patch"
|
||||
|
||||
- name: Create Pull Request
|
||||
id: create_pr
|
||||
uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v8.1.1
|
||||
with:
|
||||
token: ${{ github.token }}
|
||||
base: ${{ github.event.repository.default_branch }}
|
||||
commit-message: "Clean up ${{ needs.analyze.outputs.flag_name }} feature flag"
|
||||
branch: "oz-agent/cleanup-feature-flag-${{ needs.analyze.outputs.flag_name }}"
|
||||
author: "Oz Agent <oz-agent@warp.dev>"
|
||||
delete-branch: true
|
||||
reviewers: ${{ needs.analyze.outputs.reviewers }}
|
||||
title: "Clean up ${{ needs.analyze.outputs.flag_name }} feature flag"
|
||||
body: |
|
||||
Automated cleanup of the `${{ needs.analyze.outputs.flag_name }}` feature flag.
|
||||
|
||||
This PR was generated by the feature flag cleanup workflow.
|
||||
|
||||
- run: |
|
||||
echo "Full feature flag analysis results: ${{ needs.analyze.outputs.artifact_url }}"
|
||||
echo "Pull request created: ${{ steps.create_pr.outputs.pull-request-url }}"
|
||||
|
||||
- name: Send Slack notification
|
||||
uses: slackapi/slack-github-action@af78098f536edbc4de71162a307590698245be95 # v3.0.1
|
||||
if: ${{ steps.create_pr.outputs.pull-request-operation == 'created' }}
|
||||
with:
|
||||
method: chat.postMessage
|
||||
token: ${{ secrets.ACTION_MONITORING_SLACK }}
|
||||
payload: |
|
||||
channel: "#oncall-client"
|
||||
# Follows https://api.slack.com/reference/surfaces/formatting#mentioning-users.
|
||||
# S09SSGE729E is the user group ID for @eng-warp-3.
|
||||
text: "🧹 New feature flag cleanup PR for `${{ needs.analyze.outputs.flag_name }}`: ${{ steps.create_pr.outputs.pull-request-url }}! <!subteam^S09SSGE729E> please review\nSee logs at https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"
|
||||
@@ -0,0 +1,26 @@
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
# Potentially run this workflow whenever the master branch changes in,
|
||||
# order to update the cache if necessary.
|
||||
- master
|
||||
paths:
|
||||
# We only need to run this if there were changes to files that affect
|
||||
# the cache key.
|
||||
- 'Cargo.lock'
|
||||
- '**/Cargo.toml'
|
||||
- '.cargo/config.toml'
|
||||
- 'rust-toolchain.toml'
|
||||
# Run once per day to ensure the cache is populated even if we haven't
|
||||
# modified Cargo.lock or Cargo.toml in a while.
|
||||
schedule:
|
||||
- cron: "0 8 * * 1-5" # Run every weekday at 3am EST.
|
||||
workflow_dispatch:
|
||||
|
||||
name: Populate Build Cache
|
||||
|
||||
jobs:
|
||||
populate_build_cache:
|
||||
name: CI
|
||||
uses: ./.github/workflows/ci.yml
|
||||
secrets: inherit
|
||||
@@ -0,0 +1,44 @@
|
||||
{
|
||||
"documentation": "See the README.md file in this directory.",
|
||||
"channels": [
|
||||
{
|
||||
"channel": "dev",
|
||||
"type": "nightly",
|
||||
"is_prerelease": true,
|
||||
"is_autopush": true,
|
||||
"release_base_name": "Dev Release",
|
||||
"release_body_text": "Nightly Warp Dev release",
|
||||
"sentry_project": "warp-client-dev",
|
||||
"sentry_environment": "dev_release",
|
||||
"changelog_slack_channel": "#dev-beta-changelogs",
|
||||
"gcs_cache_control_value": "private, max-age=604800, immutable",
|
||||
"web_gcs_bucket_prefix": "warp-server-staging"
|
||||
},
|
||||
{
|
||||
"channel": "preview",
|
||||
"type": "weekly",
|
||||
"is_prerelease": false,
|
||||
"is_autopush": false,
|
||||
"release_base_name": "Preview Release",
|
||||
"release_body_text": "Warp Preview release",
|
||||
"sentry_project": "warp-client-beta-stable",
|
||||
"sentry_environment": "preview_release",
|
||||
"changelog_slack_channel": "#dev-beta-changelogs",
|
||||
"gcs_cache_control_value": "public, max-age=604800, immutable",
|
||||
"web_gcs_bucket_prefix": "warp"
|
||||
},
|
||||
{
|
||||
"channel": "stable",
|
||||
"type": "weekly",
|
||||
"is_prerelease": false,
|
||||
"is_autopush": false,
|
||||
"release_base_name": "Stable Release",
|
||||
"release_body_text": "Warp Stable release",
|
||||
"sentry_project": "warp-client-beta-stable",
|
||||
"sentry_environment": "stable_release",
|
||||
"changelog_slack_channel": "#release",
|
||||
"gcs_cache_control_value": "public, max-age=604800, immutable",
|
||||
"web_gcs_bucket_prefix": "warp"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
name: Remove Stale Issue Labels on Plan Approved (Local)
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [labeled]
|
||||
concurrency:
|
||||
group: remove-stale-labels-${{ github.event.pull_request.number || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
jobs:
|
||||
remove_stale_labels:
|
||||
if: github.event.label.name == 'plan-approved'
|
||||
permissions:
|
||||
contents: read
|
||||
issues: write
|
||||
pull-requests: read
|
||||
uses: warpdotdev/oz-for-oss/.github/workflows/remove-stale-issue-labels-on-plan-approved.yml@main
|
||||
with:
|
||||
pr_number: ${{ github.event.pull_request.number }}
|
||||
secrets:
|
||||
OZ_MGMT_GHA_APP_ID: ${{ secrets.OZ_MGMT_GHA_APP_ID }}
|
||||
OZ_MGMT_GHA_PRIVATE_KEY: ${{ secrets.OZ_MGMT_GHA_PRIVATE_KEY }}
|
||||
@@ -0,0 +1,74 @@
|
||||
name: Repo Sync
|
||||
on:
|
||||
push:
|
||||
branches: [master]
|
||||
pull_request:
|
||||
types: [closed, opened, synchronize, edited, labeled]
|
||||
branches: [master]
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
action:
|
||||
description: "Which action to run"
|
||||
required: true
|
||||
type: choice
|
||||
options: [sync, escalation]
|
||||
|
||||
jobs:
|
||||
sync:
|
||||
if: github.event_name == 'push' || inputs.action == 'sync'
|
||||
uses: warpdotdev/repo-sync/.github/workflows/sync.yml@main
|
||||
with:
|
||||
repo_sync_ref: main
|
||||
app_id: ${{ vars.REPO_SYNC_APP_ID }}
|
||||
public_repo: warpdotdev/warp
|
||||
private_repo: warpdotdev/warp-internal
|
||||
secrets:
|
||||
app_private_key: ${{ secrets.REPO_SYNC_APP_PRIVATE_KEY }}
|
||||
warp_api_key: ${{ secrets.WARP_API_KEY }}
|
||||
|
||||
restack:
|
||||
# Run the restack workflow on PRs for `repo-sync/`-prefixed branches
|
||||
# when they merge or are labelled with `repo-sync:needs-restack`.
|
||||
if: >-
|
||||
github.event_name == 'pull_request' &&
|
||||
startsWith(github.event.pull_request.head.ref, 'repo-sync/') &&
|
||||
(
|
||||
github.event.pull_request.merged == true ||
|
||||
(github.event.action == 'labeled' && github.event.label.name == 'repo-sync:needs-restack')
|
||||
)
|
||||
uses: warpdotdev/repo-sync/.github/workflows/restack.yml@main
|
||||
with:
|
||||
repo_sync_ref: main
|
||||
app_id: ${{ vars.REPO_SYNC_APP_ID }}
|
||||
public_repo: warpdotdev/warp
|
||||
private_repo: warpdotdev/warp-internal
|
||||
secrets:
|
||||
app_private_key: ${{ secrets.REPO_SYNC_APP_PRIVATE_KEY }}
|
||||
warp_api_key: ${{ secrets.WARP_API_KEY }}
|
||||
|
||||
approve:
|
||||
if: >-
|
||||
github.event_name == 'pull_request' &&
|
||||
github.event.action != 'closed' &&
|
||||
startsWith(github.event.pull_request.head.ref, 'repo-sync/')
|
||||
uses: warpdotdev/repo-sync/.github/workflows/approve.yml@main
|
||||
with:
|
||||
repo_sync_ref: main
|
||||
approver_app_id: ${{ vars.REPO_SYNC_APPROVER_APP_ID }}
|
||||
public_repo: warpdotdev/warp
|
||||
private_repo: warpdotdev/warp-internal
|
||||
secrets:
|
||||
approver_app_private_key: ${{ secrets.REPO_SYNC_APPROVER_APP_PRIVATE_KEY }}
|
||||
|
||||
escalation:
|
||||
if: inputs.action == 'escalation'
|
||||
uses: warpdotdev/repo-sync/.github/workflows/escalation.yml@main
|
||||
with:
|
||||
repo_sync_ref: main
|
||||
app_id: ${{ vars.REPO_SYNC_APP_ID }}
|
||||
public_repo: warpdotdev/warp
|
||||
private_repo: warpdotdev/warp-internal
|
||||
escalate_to: "@oncall-client-primary"
|
||||
escalate_after: "30m"
|
||||
secrets:
|
||||
app_private_key: ${{ secrets.REPO_SYNC_APP_PRIVATE_KEY }}
|
||||
@@ -0,0 +1,24 @@
|
||||
name: Respond to PR Comment (Local)
|
||||
on:
|
||||
issue_comment:
|
||||
types: [created]
|
||||
pull_request_review_comment:
|
||||
types: [created]
|
||||
pull_request_review:
|
||||
types: [submitted]
|
||||
jobs:
|
||||
# Mention, bot, event-type, and trust gates all live in the reusable
|
||||
# workflow (``respond-to-pr-comment.yml``). This adapter exists only
|
||||
# to subscribe to the three GitHub events that can carry an
|
||||
# ``@oz-agent`` mention on a PR and delegate them through
|
||||
# ``workflow_call``.
|
||||
respond:
|
||||
permissions:
|
||||
contents: write
|
||||
issues: write
|
||||
pull-requests: write
|
||||
uses: warpdotdev/oz-for-oss/.github/workflows/respond-to-pr-comment.yml@main
|
||||
secrets:
|
||||
OZ_MGMT_GHA_APP_ID: ${{ secrets.OZ_MGMT_GHA_APP_ID }}
|
||||
OZ_MGMT_GHA_PRIVATE_KEY: ${{ secrets.OZ_MGMT_GHA_PRIVATE_KEY }}
|
||||
WARP_API_KEY: ${{ secrets.OSS_WARP_API_KEY }}
|
||||
@@ -0,0 +1,22 @@
|
||||
name: Respond to Triaged Issue Comment (Local)
|
||||
on:
|
||||
issue_comment:
|
||||
types: [created]
|
||||
concurrency:
|
||||
group: respond-to-triaged-issue-comment-${{ github.event.comment.id || github.run_id }}
|
||||
cancel-in-progress: false
|
||||
jobs:
|
||||
# Mention, bot, event-type, and trust gates all live in the reusable
|
||||
# workflow (``respond-to-triaged-issue-comment.yml``). This adapter
|
||||
# exists only to subscribe to the GitHub event that can carry an
|
||||
# ``@oz-agent`` mention on a triaged issue and delegate it through
|
||||
# ``workflow_call``.
|
||||
respond_inline:
|
||||
permissions:
|
||||
contents: read
|
||||
issues: write
|
||||
uses: warpdotdev/oz-for-oss/.github/workflows/respond-to-triaged-issue-comment.yml@main
|
||||
secrets:
|
||||
OZ_MGMT_GHA_APP_ID: ${{ secrets.OZ_MGMT_GHA_APP_ID }}
|
||||
OZ_MGMT_GHA_PRIVATE_KEY: ${{ secrets.OZ_MGMT_GHA_PRIVATE_KEY }}
|
||||
WARP_API_KEY: ${{ secrets.OSS_WARP_API_KEY }}
|
||||
@@ -0,0 +1,141 @@
|
||||
name: Review Pull Request
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
pr_number:
|
||||
description: Pull request number to review
|
||||
required: false
|
||||
default: ""
|
||||
type: string
|
||||
trigger_source:
|
||||
description: Source that requested the review
|
||||
required: false
|
||||
default: ""
|
||||
type: string
|
||||
requester:
|
||||
description: Login of the user who requested the review, if any
|
||||
required: false
|
||||
default: ""
|
||||
type: string
|
||||
focus:
|
||||
description: Optional extra focus guidance for the review
|
||||
required: false
|
||||
default: ""
|
||||
type: string
|
||||
comment_id:
|
||||
description: Issue comment ID to react to for slash-command reviews
|
||||
required: false
|
||||
default: ""
|
||||
type: string
|
||||
secrets:
|
||||
OZ_MGMT_GHA_APP_ID:
|
||||
required: true
|
||||
OZ_MGMT_GHA_PRIVATE_KEY:
|
||||
required: true
|
||||
OSS_WARP_API_KEY:
|
||||
required: true
|
||||
pull_request_target:
|
||||
types:
|
||||
- opened
|
||||
- ready_for_review
|
||||
- review_requested
|
||||
- labeled
|
||||
jobs:
|
||||
resolve:
|
||||
runs-on: ubuntu-slim
|
||||
permissions:
|
||||
contents: read
|
||||
outputs:
|
||||
should_run: ${{ steps.resolve.outputs.should_run }}
|
||||
pr_number: ${{ steps.resolve.outputs.pr_number }}
|
||||
trigger_source: ${{ steps.resolve.outputs.trigger_source }}
|
||||
requester: ${{ steps.resolve.outputs.requester }}
|
||||
focus: ${{ steps.resolve.outputs.focus }}
|
||||
comment_id: ${{ steps.resolve.outputs.comment_id }}
|
||||
skip_reason: ${{ steps.resolve.outputs.skip_reason }}
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
- name: Resolve review context
|
||||
id: resolve
|
||||
env:
|
||||
INPUT_PR_NUMBER: ${{ inputs.pr_number || '' }}
|
||||
INPUT_TRIGGER_SOURCE: ${{ inputs.trigger_source || '' }}
|
||||
INPUT_REQUESTER: ${{ inputs.requester || '' }}
|
||||
INPUT_FOCUS: ${{ inputs.focus || '' }}
|
||||
INPUT_COMMENT_ID: ${{ inputs.comment_id || '' }}
|
||||
GITHUB_ACTOR_LOGIN: ${{ github.actor }}
|
||||
run: |
|
||||
python - <<'PY'
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
event = json.loads(Path(os.environ["GITHUB_EVENT_PATH"]).read_text())
|
||||
event_name = os.environ.get("GITHUB_EVENT_NAME", "")
|
||||
input_pr_number = os.environ.get("INPUT_PR_NUMBER", "").strip()
|
||||
pr = event.get("pull_request") or {}
|
||||
head_ref = ((pr.get("head") or {}).get("ref") or "").strip()
|
||||
action = (event.get("action") or "").strip()
|
||||
requested_reviewer = ((event.get("requested_reviewer") or {}).get("login") or "").strip()
|
||||
label_name = ((event.get("label") or {}).get("name") or "").strip()
|
||||
has_pr_hooks = Path(".github/workflows/pr-hooks.yml").exists()
|
||||
trigger_source = os.environ.get("INPUT_TRIGGER_SOURCE", "").strip() or event_name
|
||||
requester = os.environ.get("INPUT_REQUESTER", "").strip() or os.environ.get("GITHUB_ACTOR_LOGIN", "")
|
||||
focus = os.environ.get("INPUT_FOCUS", "")
|
||||
comment_id = os.environ.get("INPUT_COMMENT_ID", "")
|
||||
pr_number = input_pr_number or str(pr.get("number") or "")
|
||||
matches_direct_trigger = (
|
||||
(action == "opened" and not pr.get("draft", False))
|
||||
or action == "ready_for_review"
|
||||
or (action == "review_requested" and requested_reviewer == "oz-agent")
|
||||
or (action == "labeled" and label_name == "oz-review")
|
||||
)
|
||||
if input_pr_number:
|
||||
should_run = True
|
||||
elif head_ref.startswith("cherrypick"):
|
||||
should_run = False
|
||||
elif has_pr_hooks and event_name == "pull_request_target":
|
||||
should_run = False
|
||||
else:
|
||||
should_run = matches_direct_trigger and bool(pr_number)
|
||||
with open(os.environ["GITHUB_OUTPUT"], "a", encoding="utf-8") as fh:
|
||||
fh.write(f"should_run={'true' if should_run else 'false'}\n")
|
||||
fh.write(f"pr_number={pr_number}\n")
|
||||
fh.write(f"trigger_source={trigger_source}\n")
|
||||
fh.write(f"requester={requester}\n")
|
||||
fh.write("focus<<__EOF__\n")
|
||||
fh.write(focus)
|
||||
fh.write("\n__EOF__\n")
|
||||
fh.write(f"comment_id={comment_id}\n")
|
||||
if has_pr_hooks and event_name == "pull_request_target" and not input_pr_number:
|
||||
fh.write("skip_reason=pr-hooks-present\n")
|
||||
elif head_ref.startswith("cherrypick"):
|
||||
fh.write("skip_reason=cherrypick-branch\n")
|
||||
elif not should_run:
|
||||
fh.write("skip_reason=event-not-enabled\n")
|
||||
PY
|
||||
skip_direct_trigger:
|
||||
needs: resolve
|
||||
if: needs.resolve.outputs.should_run != 'true' && needs.resolve.outputs.skip_reason == 'pr-hooks-present'
|
||||
runs-on: ubuntu-slim
|
||||
steps:
|
||||
- name: Explain skip
|
||||
run: echo "PR review orchestration skipped because .github/workflows/pr-hooks.yml is present."
|
||||
review_pr:
|
||||
needs: resolve
|
||||
if: needs.resolve.outputs.should_run == 'true'
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
issues: write
|
||||
uses: warpdotdev/oz-for-oss/.github/workflows/review-pull-request.yml@main
|
||||
with:
|
||||
pr_number: ${{ needs.resolve.outputs.pr_number }}
|
||||
trigger_source: ${{ needs.resolve.outputs.trigger_source }}
|
||||
requester: ${{ needs.resolve.outputs.requester }}
|
||||
focus: ${{ needs.resolve.outputs.focus }}
|
||||
comment_id: ${{ needs.resolve.outputs.comment_id }}
|
||||
secrets:
|
||||
OZ_MGMT_GHA_APP_ID: ${{ secrets.OZ_MGMT_GHA_APP_ID }}
|
||||
OZ_MGMT_GHA_PRIVATE_KEY: ${{ secrets.OZ_MGMT_GHA_PRIVATE_KEY }}
|
||||
WARP_API_KEY: ${{ secrets.OSS_WARP_API_KEY }}
|
||||
@@ -0,0 +1,32 @@
|
||||
# Fails a required status check if a pull request from one `repo-sync/*`
|
||||
# branch targets another `repo-sync/*` branch. Combined with branch
|
||||
# protection on `repo-sync/**` requiring this check, this prevents one sync
|
||||
# PR from being merged into another sync PR's branch (which would bypass the
|
||||
# restacking flow and corrupt the sync stack).
|
||||
#
|
||||
# PRs from non-`repo-sync/*` branches into `repo-sync/*` branches (e.g. for
|
||||
# conflict resolution) are intentionally allowed. Direct pushes to
|
||||
# `repo-sync/*` branches are also unaffected, since only the `pull_request`
|
||||
# trigger is used here.
|
||||
|
||||
name: Sync PR Checks
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, reopened, edited, synchronize, ready_for_review]
|
||||
|
||||
jobs:
|
||||
check:
|
||||
name: Verify PR base is the default branch
|
||||
runs-on: ubuntu-slim
|
||||
steps:
|
||||
- name: Fail if merging a repo-sync branch into another repo-sync branch
|
||||
env:
|
||||
BASE_REF: ${{ github.base_ref }}
|
||||
HEAD_REF: ${{ github.head_ref }}
|
||||
run: |
|
||||
if [[ "$BASE_REF" == repo-sync/* && "$HEAD_REF" == repo-sync/* ]]; then
|
||||
echo "::error::repo-sync/* PRs cannot be merged into other repo-sync/* branches."
|
||||
echo "Wait for repo-sync to retarget this PR to the default branch before merging."
|
||||
exit 1
|
||||
fi
|
||||
echo "Base branch '$BASE_REF' and head branch '$HEAD_REF' are OK."
|
||||
@@ -0,0 +1,60 @@
|
||||
name: Triage New Issues (Local)
|
||||
on:
|
||||
issues:
|
||||
types: [opened]
|
||||
issue_comment:
|
||||
types: [created]
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
issue_number:
|
||||
description: Optional issue number to triage immediately
|
||||
required: false
|
||||
default: ''
|
||||
type: string
|
||||
lookback_minutes:
|
||||
description: Minutes of issue history to scan when no issue number is provided
|
||||
required: false
|
||||
default: '60'
|
||||
type: string
|
||||
concurrency:
|
||||
group: triage-new-issues-${{ github.event.issue.number || inputs.issue_number || github.run_id }}
|
||||
cancel-in-progress: false
|
||||
jobs:
|
||||
triage_issues:
|
||||
# A needs-info reply by the original reporter triggers re-triage
|
||||
# even if it mentions @oz-agent, because the respond-to-triaged
|
||||
# workflow handles explicit mentions on triaged issues separately.
|
||||
if: |
|
||||
(
|
||||
github.event_name != 'issue_comment' &&
|
||||
!contains(github.event.issue.labels.*.name, 'triaged') &&
|
||||
!contains(github.event.issue.labels.*.name, 'ready-to-spec') &&
|
||||
!contains(github.event.issue.labels.*.name, 'ready-to-implement')
|
||||
) || (
|
||||
github.event_name == 'issue_comment' &&
|
||||
!github.event.issue.pull_request &&
|
||||
github.event.comment.user.type != 'Bot' &&
|
||||
!endsWith(github.event.comment.user.login, '[bot]') &&
|
||||
(
|
||||
(
|
||||
contains(github.event.comment.body, '@oz-agent') &&
|
||||
!contains(github.event.issue.labels.*.name, 'triaged')
|
||||
) ||
|
||||
(
|
||||
contains(github.event.issue.labels.*.name, 'needs-info') &&
|
||||
github.event.comment.user.login == github.event.issue.user.login &&
|
||||
!contains(github.event.comment.body, '@oz-agent')
|
||||
)
|
||||
)
|
||||
)
|
||||
permissions:
|
||||
contents: read
|
||||
issues: write
|
||||
uses: warpdotdev/oz-for-oss/.github/workflows/triage-new-issues.yml@main
|
||||
with:
|
||||
issue_number: ${{ github.event.issue.number || github.event.inputs.issue_number || '' }}
|
||||
lookback_minutes: ${{ github.event.inputs.lookback_minutes || '60' }}
|
||||
secrets:
|
||||
OZ_MGMT_GHA_APP_ID: ${{ secrets.OZ_MGMT_GHA_APP_ID }}
|
||||
OZ_MGMT_GHA_PRIVATE_KEY: ${{ secrets.OZ_MGMT_GHA_PRIVATE_KEY }}
|
||||
WARP_API_KEY: ${{ secrets.OSS_WARP_API_KEY }}
|
||||
@@ -0,0 +1,22 @@
|
||||
name: Trigger Implementation on Plan Approved (Local)
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [labeled]
|
||||
concurrency:
|
||||
group: trigger-impl-plan-approved-${{ github.event.pull_request.number || github.run_id }}
|
||||
cancel-in-progress: false
|
||||
jobs:
|
||||
trigger_implementation:
|
||||
if: >-
|
||||
github.event.label.name == 'plan-approved' &&
|
||||
github.event.pull_request.state == 'open'
|
||||
name: Trigger implementation for approved plan
|
||||
permissions:
|
||||
contents: write
|
||||
issues: write
|
||||
pull-requests: write
|
||||
uses: warpdotdev/oz-for-oss/.github/workflows/trigger-implementation-on-plan-approved.yml@main
|
||||
secrets:
|
||||
OZ_MGMT_GHA_APP_ID: ${{ secrets.OZ_MGMT_GHA_APP_ID }}
|
||||
OZ_MGMT_GHA_PRIVATE_KEY: ${{ secrets.OZ_MGMT_GHA_PRIVATE_KEY }}
|
||||
WARP_API_KEY: ${{ secrets.OSS_WARP_API_KEY }}
|
||||
@@ -0,0 +1,23 @@
|
||||
name: Update Dedupe Skill (Local)
|
||||
on:
|
||||
schedule:
|
||||
- cron: '30 9 * * 1' # Every Monday at 09:30 UTC
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
lookback_days:
|
||||
description: Number of days to look back for closed-as-duplicate signals
|
||||
required: false
|
||||
default: '7'
|
||||
type: string
|
||||
jobs:
|
||||
update_dedupe:
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
uses: warpdotdev/oz-for-oss/.github/workflows/update-dedupe.yml@main
|
||||
with:
|
||||
lookback_days: ${{ github.event.inputs.lookback_days || '7' }}
|
||||
secrets:
|
||||
OZ_MGMT_GHA_APP_ID: ${{ secrets.OZ_MGMT_GHA_APP_ID }}
|
||||
OZ_MGMT_GHA_PRIVATE_KEY: ${{ secrets.OZ_MGMT_GHA_PRIVATE_KEY }}
|
||||
WARP_API_KEY: ${{ secrets.OSS_WARP_API_KEY }}
|
||||
@@ -0,0 +1,23 @@
|
||||
name: Update PR Review Skill (Local)
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 9 * * 1' # Every Monday at 09:00 UTC
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
lookback_days:
|
||||
description: Number of days to look back for PR feedback
|
||||
required: false
|
||||
default: '7'
|
||||
type: string
|
||||
jobs:
|
||||
update_pr_review:
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
uses: warpdotdev/oz-for-oss/.github/workflows/update-pr-review.yml@main
|
||||
with:
|
||||
lookback_days: ${{ github.event.inputs.lookback_days || '7' }}
|
||||
secrets:
|
||||
OZ_MGMT_GHA_APP_ID: ${{ secrets.OZ_MGMT_GHA_APP_ID }}
|
||||
OZ_MGMT_GHA_PRIVATE_KEY: ${{ secrets.OZ_MGMT_GHA_PRIVATE_KEY }}
|
||||
WARP_API_KEY: ${{ secrets.OSS_WARP_API_KEY }}
|
||||
@@ -0,0 +1,23 @@
|
||||
name: Update Triage Skill (Local)
|
||||
on:
|
||||
schedule:
|
||||
- cron: '15 9 * * 1' # Every Monday at 09:15 UTC
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
lookback_days:
|
||||
description: Number of days to look back for triage feedback
|
||||
required: false
|
||||
default: '7'
|
||||
type: string
|
||||
jobs:
|
||||
update_triage:
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
uses: warpdotdev/oz-for-oss/.github/workflows/update-triage.yml@main
|
||||
with:
|
||||
lookback_days: ${{ github.event.inputs.lookback_days || '7' }}
|
||||
secrets:
|
||||
OZ_MGMT_GHA_APP_ID: ${{ secrets.OZ_MGMT_GHA_APP_ID }}
|
||||
OZ_MGMT_GHA_PRIVATE_KEY: ${{ secrets.OZ_MGMT_GHA_PRIVATE_KEY }}
|
||||
WARP_API_KEY: ${{ secrets.OSS_WARP_API_KEY }}
|
||||
@@ -0,0 +1,18 @@
|
||||
name: Verify PR Comment (Local)
|
||||
on:
|
||||
issue_comment:
|
||||
types: [created]
|
||||
jobs:
|
||||
# Slash-command parsing, bot gating, and trust admission live in the
|
||||
# reusable workflow. This local adapter only subscribes to PR issue
|
||||
# comments and delegates through ``workflow_call``.
|
||||
verify:
|
||||
permissions:
|
||||
contents: read
|
||||
issues: write
|
||||
pull-requests: write
|
||||
uses: warpdotdev/oz-for-oss/.github/workflows/verify-pr-comment.yml@main
|
||||
secrets:
|
||||
OZ_MGMT_GHA_APP_ID: ${{ secrets.OZ_MGMT_GHA_APP_ID }}
|
||||
OZ_MGMT_GHA_PRIVATE_KEY: ${{ secrets.OZ_MGMT_GHA_PRIVATE_KEY }}
|
||||
WARP_API_KEY: ${{ secrets.OSS_WARP_API_KEY }}
|
||||
@@ -0,0 +1,71 @@
|
||||
|
||||
# ======================================================================================
|
||||
# Workflow: Cleanup Fix PRs
|
||||
# ======================================================================================
|
||||
# Usage:
|
||||
# - This workflow cleans up PRs generated by Fix Failing Checks
|
||||
#
|
||||
# Expected Output:
|
||||
# - The workflow should close all PRs created by the github-actions bot that are based on a branch with the auto-fix-ci label after it closes
|
||||
#
|
||||
# ======================================================================================
|
||||
|
||||
name: Cleanup Fix PRs
|
||||
on:
|
||||
pull_request:
|
||||
types: [closed]
|
||||
|
||||
jobs:
|
||||
close-fix-prs:
|
||||
runs-on: ubuntu-latest
|
||||
if: contains(github.event.pull_request.labels.*.name, 'auto-fix-ci')
|
||||
steps:
|
||||
- name: Close Dependent Fix PRs
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
||||
with:
|
||||
script: |
|
||||
const { pull_request: pr, repository } = context.payload;
|
||||
const { owner, repo } = context.repo;
|
||||
|
||||
const closedBranch = pr.head.ref;
|
||||
console.log(`Original PR #${pr.number} closed. Head ref was: ${closedBranch}`);
|
||||
|
||||
// Find open PRs that target the closed PR's branch
|
||||
const { data: openPRs } = await github.rest.pulls.list({
|
||||
owner,
|
||||
repo,
|
||||
state: 'open',
|
||||
base: closedBranch
|
||||
});
|
||||
|
||||
if (openPRs.length === 0) {
|
||||
console.log('No open fix PRs found targeting this branch.');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`Found ${openPRs.length} open PRs targeting ${closedBranch}. Closing them...`);
|
||||
|
||||
for (const openPR of openPRs) {
|
||||
if (openPR.user.login !== 'github-actions[bot]') {
|
||||
console.log(`Skipping PR #${openPR.number} created by ${openPR.user.login} (not github-actions[bot])`);
|
||||
continue;
|
||||
}
|
||||
|
||||
console.log(`Closing PR #${openPR.number}: ${openPR.title}`);
|
||||
|
||||
// Comment on the fix PR
|
||||
await github.rest.issues.createComment({
|
||||
owner,
|
||||
repo,
|
||||
issue_number: openPR.number,
|
||||
body: `Closing this fix PR because the parent PR #${pr.number} has been merged.`
|
||||
});
|
||||
|
||||
// Close the fix PR
|
||||
await github.rest.pulls.update({
|
||||
owner,
|
||||
repo,
|
||||
pull_number: openPR.number,
|
||||
state: 'closed'
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user