Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions .config/nextest.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Test profiles for cargo-nextest.
#
# `pr` is the required pull-request gate. It skips the tests below, each of which takes 1 s or
# more on its own because it drives real child processes, PTYs or a resumed CLI run. The full
# suite (`default`) still runs on every push to main, on every platform, and in the release
# quality gate. Regenerate the list with `cargo nextest run --workspace -j 1` and keep tests
# that report >= 1 s.

[profile.pr]
default-filter = """
not (
test(=allow_process::tests::executable_specs_are_absolute_deterministic_and_path_redacted) |
test(=bare_xgen_streams_durable_progress_prompts_separate_approvals_and_replays_offline) |
test(=catalog::tests::executable_alias_is_launch_preserved_digest_bound_and_retarget_checked) |
test(=catalog::tests::executable_fingerprints_detect_content_changes_without_exposing_paths) |
test(=changed_safe_environment_is_named_and_blocks_resume_until_restored) |
test(=completion_checks_require_latest_success_after_changes_on_design_and_held_out_inputs) |
test(=composition::tests::execute_flag_approves_only_the_exact_process_catalog_request_shape) |
test(=composition::tests::product_registry_is_bound_to_the_exact_workspace) |
test(=composition::tests::reusable_process_session_is_bound_to_one_physical_workspace) |
test(=composition::tests::search_approval_requires_catalogued_backend_exact_query_and_once_lifetime) |
test(=composition::tests::terminal_approval_is_catalogued_exact_once_and_execute_gated) |
test(=conversation_only_requests_need_no_tool_approvals_and_replay_offline) |
test(=execution::tests::catalogued_symlink_proxy_preserves_argv_zero_alias) |
test(=execution::tests::escaped_stdio_never_blocks_past_the_capture_grace) |
test(=execution::tests::nonzero_exit_and_bounded_streams_are_durable_result_data) |
test(=execution::tests::normal_leader_exit_also_terminates_the_descendant_tree) |
test(=execution::tests::prepared_debug_redacts_command_material_and_rejects_protected_env) |
test(=execution::tests::relative_cwd_and_explicit_environment_reach_the_child) |
test(=execution::tests::shell_metacharacters_are_one_literal_argument) |
test(=execution::tests::timeout_terminates_the_descendant_tree) |
test(=explicit_recovery_preserves_budget_and_verified_effect_before_separate_resume) |
test(=final_contract_projects_process_arguments_exits_and_actual_order_then_replays_offline) |
test(=final_contract_rejects_prose_missing_fields_and_model_written_commands_after_read) |
test(=invocation_diagnostics_distinguish_schema_and_resource_failures_without_values) |
test(=path::tests::cwd_never_follows_a_workspace_symlink) |
test(=path::tests::cwd_rejects_an_ambient_root_retargeted_after_preopen) |
test(=process_execution_requires_separate_approval_and_resumes_without_replay) |
test(=process_outcome_commit_failure_recovers_without_reexecution) |
test(=pty_cli_approves_start_commits_input_output_receipts_and_replays_offline) |
test(=rejected_final_response_resumes_after_restart_without_replaying_writes_or_failed_command) |
test(=sigint_during_model_call_stops_at_a_safe_or_unknown_no_replay_boundary) |
test(=slow_provider_becomes_a_durable_timeout_without_retry) |
test(=terminal::sessions::tests::literal_input_unicode_output_and_non_consuming_offsets) |
test(=terminal::sessions::tests::nonzero_timeout_and_missing_session_remain_observations) |
test(=terminal::sessions::tests::output_ring_is_bounded_and_reports_lost_bytes) |
test(=terminal::sessions::tests::repeated_start_is_not_respawned_and_session_count_is_bounded) |
test(=terminal::sessions::tests::terminal_control_bytes_and_write_deadlines_are_supported) |
test(=terminal::sessions::tests::termination_and_leader_exit_reach_jobs_in_their_own_groups) |
test(=terminal::sessions::tests::termination_normal_exit_and_host_drop_clean_owned_descendants) |
test(=tests::sqlite_cold_audit_indexes_each_event_once_and_warm_append_skips_historical_rows) |
test(=tests::sqlite_receipt_heavy_run_uses_one_lookup_per_binding_and_no_warm_history_scan) |
test(=tests::sqlite_ten_thousand_event_run_has_bounded_warm_verification_and_linear_cold_audit) |
test(=tests::workspace_debug_and_binding_never_expose_ambient_values) |
test(=tool_discovery_is_approved_receipted_and_frozen_across_resume) |
test(=usage_counts_rejected_responses_and_survives_resume_without_recounting_replay)
)
"""
56 changes: 56 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,20 +1,75 @@
name: CI

# Pull requests run only the fast Linux gate (about a minute with a warm cache). The full
# quality and five-platform jobs run after merge on main, on manual dispatch, and again in the
# release workflow before anything is published.
on:
pull_request:
push:
branches: [main]
workflow_dispatch:

permissions:
contents: read

concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

env:
CC_aarch64_unknown_linux_musl: musl-gcc
CC_x86_64_unknown_linux_musl: musl-gcc

jobs:
fast:
name: Fast / Linux
runs-on: ubuntu-24.04
env:
CARGO_INCREMENTAL: "0"
CARGO_PROFILE_DEV_DEBUG: "0"
CARGO_PROFILE_TEST_DEBUG: "0"
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false

- name: Install pinned Rust toolchain
run: rustup toolchain install --no-self-update

- name: Restore Rust build cache
uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
with:
save-if: ${{ github.ref == 'refs/heads/main' }}

- name: Install cargo-nextest
uses: taiki-e/install-action@1168f6810a3cb30a3514bf5756a3b7f0422c6df8 # v2.87.28
with:
tool: cargo-nextest@0.9.148

- name: Check formatting
run: cargo fmt --all -- --check

- name: Lint workspace
run: cargo clippy --workspace --all-targets --locked -- -D warnings

- name: Test workspace without slow process tests
run: cargo nextest run --profile pr --workspace --locked

- name: Test Python scripts without API calls
run: python3 -m unittest discover -s scripts/tests

- name: Check release workflows and public documentation
shell: bash
run: |
set -euo pipefail
sh scripts/check-release-workflow.sh
sh scripts/check-npm-distribution-workflow.sh
sh scripts/check-rc3-public-docs.sh

quality:
name: Quality / Linux
if: github.event_name != 'pull_request'
runs-on: ubuntu-24.04
steps:
- name: Checkout
Expand Down Expand Up @@ -72,6 +127,7 @@ jobs:

platform:
name: Platform / ${{ matrix.target }}
if: github.event_name != 'pull_request'
strategy:
fail-fast: false
matrix:
Expand Down
38 changes: 30 additions & 8 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ jobs:
quality:
name: Release quality gate
runs-on: ubuntu-24.04
permissions:
actions: read
checks: read
contents: read
steps:
- name: Validate trusted release request
env:
Expand Down Expand Up @@ -60,6 +64,31 @@ jobs:
.head_sha == $sha
' "$source_run" >/dev/null

- name: Require the full main CI to have passed on the release commit
env:
GH_TOKEN: ${{ github.token }}
RELEASE_SHA: ${{ github.event.workflow_run.head_sha }}
shell: bash
run: |
set -euo pipefail
# Pull requests only run the fast gate; the full quality and platform jobs run on main.
gh api --paginate "repos/$GITHUB_REPOSITORY/commits/$RELEASE_SHA/check-runs?per_page=100" \
--jq '.check_runs[] | select(.app.slug == "github-actions") | [.name, .conclusion // "pending"] | @tsv' \
> "$RUNNER_TEMP/release-check-runs.tsv"
for check in \
"Quality / Linux" \
"Platform / x86_64-unknown-linux-musl" \
"Platform / aarch64-unknown-linux-musl" \
"Platform / x86_64-apple-darwin" \
"Platform / aarch64-apple-darwin" \
"Platform / x86_64-pc-windows-msvc"; do
if ! awk -F '\t' -v name="$check" '$1 == name && $2 == "success" { found = 1 } END { exit !found }' \
"$RUNNER_TEMP/release-check-runs.tsv"; then
printf 'main CI check did not pass on %s: %s\n' "$RELEASE_SHA" "$check" >&2
exit 1
fi
done

- name: Checkout exact release commit
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
Expand Down Expand Up @@ -549,14 +578,7 @@ jobs:
[.required_status_checks[] |
select(.integration_id == $github_actions_integration_id) |
.context]] |
any(
(index("Quality / Linux") != null) and
(index("Platform / x86_64-unknown-linux-musl") != null) and
(index("Platform / aarch64-unknown-linux-musl") != null) and
(index("Platform / x86_64-apple-darwin") != null) and
(index("Platform / aarch64-apple-darwin") != null) and
(index("Platform / x86_64-pc-windows-msvc") != null)
))
any(index("Fast / Linux") != null))
' >/dev/null; then
main_ruleset_ok=true
break
Expand Down
18 changes: 9 additions & 9 deletions docs/development/releasing.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,15 @@ binary를 감싸는 선택형 설치 채널이며 제품 runtime을 다시 구
- `refs/tags/v*` 또는 모든 tag의 update와 deletion을 막는 active repository ruleset을
exclude와 bypass actor 없이 활성화한다.
- `main`에 PR, deletion과 non-fast-forward 차단을 적용하는 active branch ruleset을 exclude와
bypass actor 없이 활성화한다. Required status checks의 strict policy를 켜고 아래 여섯 context를
모두 필수로 지정한다. 각 check의 expected source는 반드시 `GitHub Actions`로 선택한다. 이름만 같고
source가 다르거나 source가 지정되지 않은 check는 release workflow가 거부한다.
- `Quality / Linux`
- `Platform / x86_64-unknown-linux-musl`
- `Platform / aarch64-unknown-linux-musl`
- `Platform / x86_64-apple-darwin`
- `Platform / aarch64-apple-darwin`
- `Platform / x86_64-pc-windows-msvc`
bypass actor 없이 활성화한다. Required status checks의 strict policy를 켜고 `Fast / Linux`를
필수로 지정한다. expected source는 반드시 `GitHub Actions`로 선택한다. 이름만 같고 source가
다르거나 source가 지정되지 않은 check는 release workflow가 거부한다.
- PR에서는 `Fast / Linux`만 돈다. fmt, clippy, 단독 1초 이상 걸리는 프로세스·PTY·재개 테스트를 뺀
nextest `pr` profile, Python 스크립트 테스트, release workflow 계약 검사를 한다. 나머지 여섯 job
(`Quality / Linux`, `Platform / x86_64-unknown-linux-musl`, `Platform / aarch64-unknown-linux-musl`,
`Platform / x86_64-apple-darwin`, `Platform / aarch64-apple-darwin`,
`Platform / x86_64-pc-windows-msvc`)은 main push와 수동 실행에서 전체 테스트로 돈다. release quality
gate는 release commit에서 이 여섯 check가 모두 success인지 확인한 뒤에만 진행한다.
설정 후
`XGEN_MAIN_PROTECTION_ENABLED=true`를 둔다.
- Bypass actor가 없음을 관리자가 확인한 뒤 Actions repository variable
Expand Down