mirror of
https://github.com/rojo-rbx/rojo.git
synced 2026-10-23 14:01:37 +00:00
Refactor CI pipeline scripts and documentation
- Consolidated CI pipeline steps into PowerShell and Bash scripts for improved usability and consistency. - Updated documentation in `ci.md` to clarify the execution process and options for skipping the rbx-dom section. - Enhanced error handling and reporting in the scripts to provide clearer feedback on CI step results. - Streamlined formatting and linting processes for both Rojo and rbx-dom, ensuring a more efficient CI workflow.
This commit is contained in:
parent
9688224cb4
commit
f496bee347
8 changed files with 473 additions and 193 deletions
|
|
@ -1,185 +1,50 @@
|
||||||
# /ci - Complete CI Pipeline
|
# /ci - Complete CI Pipeline
|
||||||
|
|
||||||
Run the complete CI pipeline: auto-fix all formatting, run all linters, build everything, and run ALL tests.
|
Run the complete CI pipeline by executing the appropriate platform script.
|
||||||
|
|
||||||
## Instructions
|
## Instructions
|
||||||
|
|
||||||
- If the user's message contains **"rbx-dom"**, run **both** the Rojo and rbx-dom sections below.
|
### 1. Determine rbx-dom Flag
|
||||||
- Otherwise, **skip the entire rbx-dom section** (steps 9–14) to save time.
|
|
||||||
|
|
||||||
Execute ALL applicable steps in order. Fix issues automatically where possible.
|
- By default, run **without** the skip flag (full suite including rbx-dom).
|
||||||
|
- If the user's message asks to **skip rbx-dom**, pass the skip flag (`-NoRbxDom` / `--no-rbx-dom`).
|
||||||
|
|
||||||
---
|
### 2. Run the CI Script
|
||||||
|
|
||||||
## Rojo
|
**Windows (PowerShell):**
|
||||||
|
|
||||||
### 1. Auto-fix Lua Formatting (Stylua)
|
|
||||||
```powershell
|
```powershell
|
||||||
stylua plugin/src
|
.\scripts\ci.ps1 # with rbx-dom
|
||||||
|
.\scripts\ci.ps1 -NoRbxDom # without rbx-dom
|
||||||
```
|
```
|
||||||
|
|
||||||
### 2. Auto-fix Rust Formatting
|
**macOS/Linux (Bash):**
|
||||||
```powershell
|
```bash
|
||||||
cargo fmt
|
bash scripts/ci.sh # with rbx-dom
|
||||||
|
bash scripts/ci.sh --no-rbx-dom # without rbx-dom
|
||||||
```
|
```
|
||||||
|
|
||||||
### 3. Auto-fix TypeScript Formatting (Prettier) for vscode-rojo
|
**Monitor the script output as it runs.** After each step completes, check its result. If a step failed, kill the script and switch to manual fixing (step 3) — don't let it continue to the next step.
|
||||||
```powershell
|
|
||||||
cd vscode-rojo
|
|
||||||
npx prettier --write "src/**/*.ts"
|
|
||||||
cd ..
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4. Lua Static Analysis (Selene) - MUST PASS WITH ZERO ISSUES
|
### 3. Fix Failures Between Steps
|
||||||
```powershell
|
|
||||||
selene plugin/src
|
|
||||||
```
|
|
||||||
|
|
||||||
**CRITICAL: Selene must exit with code 0 (zero errors AND zero warnings).** Warnings count as failures.
|
When a step fails, stop the script and fix the issue before continuing. Use targeted commands to speed up the feedback loop:
|
||||||
|
|
||||||
If Selene reports ANY errors or warnings:
|
1. **Formatting/linting failures** — auto-fix with the relevant formatter (`cargo fmt`, `stylua`), then verify with the specific check command only (e.g. `cargo fmt --check`).
|
||||||
1. **FIX THEM IMMEDIATELY** before proceeding - do not just report them
|
|
||||||
2. Common fixes:
|
|
||||||
- `unused_variable`: Replace with `_` (e.g., `for _ = 1, 10 do`) or remove the variable entirely
|
|
||||||
- `unused_variable` for imports: Remove the unused require/import line
|
|
||||||
3. Re-run Selene after fixes to confirm exit code 0
|
|
||||||
4. Only proceed once Selene shows: `Results: 0 errors, 0 warnings, 0 parse errors`
|
|
||||||
|
|
||||||
### 5. Rust Linting (Clippy) - Auto-fix where possible
|
2. **Test failures — investigate the implementation, not just the tests.** For every failing test:
|
||||||
```powershell
|
- Read the failing test to understand what behavior it asserts.
|
||||||
cargo clippy --fix --allow-dirty --allow-staged
|
- Read the implementation code the test exercises.
|
||||||
```
|
- Determine the root cause:
|
||||||
Then verify with:
|
- **Implementation regression/bug:** The feature is incomplete or broken. **Fix the implementation** so the test passes. Do not weaken or delete the test.
|
||||||
```powershell
|
- **Outdated test fixture:** The test's snapshot or fixture data is stale because of an intentional change. Update the fixture with `cargo insta review`/`cargo insta accept`, or regenerate the fixture data. Confirm the new fixture reflects correct behavior.
|
||||||
cargo clippy --all-targets --all-features
|
- **Incomplete feature:** A new feature was added but doesn't fully handle all cases the tests cover. **Complete the feature implementation** rather than trimming the tests to match a partial implementation.
|
||||||
```
|
- **Never** delete or gut a test just to make CI green. Tests exist for a reason — the code must satisfy them, not the other way around.
|
||||||
If there are remaining warnings/errors, fix them before proceeding.
|
- **Re-run only the failing test(s)** to verify fixes quickly (e.g. `cargo test <test_name>`, `cargo test -p <crate>`). Do not re-run the entire suite until you believe all issues are resolved.
|
||||||
|
|
||||||
### 6. Build Everything
|
3. **After all known issues are fixed**, re-run the full CI script from step 2 to catch any remaining failures.
|
||||||
```powershell
|
|
||||||
cargo build --locked --all-targets --all-features
|
|
||||||
```
|
|
||||||
|
|
||||||
### 7. Run ALL Rust Tests
|
4. Repeat until the script exits with `Overall: PASS`.
|
||||||
```powershell
|
|
||||||
cargo test --locked --all-targets --all-features
|
|
||||||
```
|
|
||||||
|
|
||||||
### 8. Run Roblox Plugin Tests
|
### 4. Report
|
||||||
|
|
||||||
**Do NOT use the scripts (`unit-test-plugin.ps1` / `unit-test-plugin.sh`).** They reference a system-installed binary which may not exist. Always use the freshly built binary from step 6.
|
Once CI passes clean, provide a brief summary of what was fixed (if anything) and confirm the final result.
|
||||||
|
|
||||||
```powershell
|
|
||||||
.\target\debug\atlas.exe build plugin/test-place.project.json -o TestPlace.rbxl
|
|
||||||
run-in-roblox --script plugin/run-tests.server.lua --place TestPlace.rbxl
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## rbx-dom (submodule)
|
|
||||||
|
|
||||||
All rbx-dom steps run inside the `rbx-dom/` directory.
|
|
||||||
|
|
||||||
### 9. Auto-fix Lua Formatting (Stylua) for rbx_dom_lua
|
|
||||||
```powershell
|
|
||||||
cd rbx-dom
|
|
||||||
stylua rbx_dom_lua/src
|
|
||||||
cd ..
|
|
||||||
```
|
|
||||||
|
|
||||||
### 10. Auto-fix Rust Formatting
|
|
||||||
```powershell
|
|
||||||
cd rbx-dom
|
|
||||||
cargo fmt
|
|
||||||
cd ..
|
|
||||||
```
|
|
||||||
|
|
||||||
### 11. Lua Static Analysis (Selene) for rbx_dom_lua - MUST PASS WITH ZERO ISSUES
|
|
||||||
```powershell
|
|
||||||
cd rbx-dom
|
|
||||||
selene rbx_dom_lua/src
|
|
||||||
cd ..
|
|
||||||
```
|
|
||||||
|
|
||||||
Same rules as step 4: exit code 0 required, fix all errors and warnings before proceeding.
|
|
||||||
|
|
||||||
### 12. Rust Linting (Clippy) - Auto-fix where possible
|
|
||||||
```powershell
|
|
||||||
cd rbx-dom
|
|
||||||
cargo clippy --fix --allow-dirty --allow-staged
|
|
||||||
```
|
|
||||||
Then verify with:
|
|
||||||
```powershell
|
|
||||||
cd rbx-dom
|
|
||||||
cargo clippy --all-targets --all-features -- -D warnings
|
|
||||||
cd ..
|
|
||||||
```
|
|
||||||
If there are remaining warnings/errors, fix them before proceeding.
|
|
||||||
|
|
||||||
### 13. Build rbx-dom
|
|
||||||
```powershell
|
|
||||||
cd rbx-dom
|
|
||||||
cargo build --verbose
|
|
||||||
cargo build --all-features --verbose
|
|
||||||
cd ..
|
|
||||||
```
|
|
||||||
|
|
||||||
### 14. Run ALL rbx-dom Rust Tests
|
|
||||||
```powershell
|
|
||||||
cd rbx-dom
|
|
||||||
cargo test --verbose
|
|
||||||
cargo test --all-features --verbose
|
|
||||||
cd ..
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Reporting
|
|
||||||
|
|
||||||
After ALL steps complete, provide a summary:
|
|
||||||
|
|
||||||
```
|
|
||||||
=== CI COMPLETE ===
|
|
||||||
|
|
||||||
--- Rojo ---
|
|
||||||
|
|
||||||
Formatting:
|
|
||||||
- Stylua: [fixed X files / no changes needed]
|
|
||||||
- Rustfmt: [fixed X files / no changes needed]
|
|
||||||
- Prettier (vscode-rojo): [fixed X files / no changes needed]
|
|
||||||
|
|
||||||
Linting:
|
|
||||||
- Selene: PASS (0 errors, 0 warnings) / FAIL (X errors, Y warnings)
|
|
||||||
- Clippy: PASS / FAIL (X warnings)
|
|
||||||
|
|
||||||
Build: PASS / FAIL
|
|
||||||
|
|
||||||
Tests:
|
|
||||||
- Rust: X passed, Y failed
|
|
||||||
- Plugin: X passed, Y failed
|
|
||||||
|
|
||||||
--- rbx-dom (only if included) ---
|
|
||||||
|
|
||||||
Formatting:
|
|
||||||
- Stylua (rbx_dom_lua): [fixed X files / no changes needed]
|
|
||||||
- Rustfmt: [fixed X files / no changes needed]
|
|
||||||
|
|
||||||
Linting:
|
|
||||||
- Selene (rbx_dom_lua): PASS (0 errors, 0 warnings) / FAIL (X errors, Y warnings)
|
|
||||||
- Clippy: PASS / FAIL (X warnings)
|
|
||||||
|
|
||||||
Build: PASS / FAIL
|
|
||||||
Build (all features): PASS / FAIL
|
|
||||||
|
|
||||||
Tests:
|
|
||||||
- Rust: X passed, Y failed
|
|
||||||
- Rust (all features): X passed, Y failed
|
|
||||||
|
|
||||||
--- Overall: PASS / FAIL ---
|
|
||||||
```
|
|
||||||
|
|
||||||
**IMPORTANT:**
|
|
||||||
- Selene PASS requires exit code 0 (0 errors AND 0 warnings). Any warnings = FAIL.
|
|
||||||
- If any step fails, continue running the remaining steps to get a complete picture, then report all failures at the end.
|
|
||||||
- Do not report "Overall: PASS" unless ALL linters pass with zero issues across all sections that were run.
|
|
||||||
- If rbx-dom was skipped, omit its section from the report entirely.
|
|
||||||
|
|
|
||||||
193
scripts/ci.ps1
Normal file
193
scripts/ci.ps1
Normal file
|
|
@ -0,0 +1,193 @@
|
||||||
|
# Complete CI Pipeline
|
||||||
|
# Usage: .\scripts\ci.ps1 [-NoRbxDom]
|
||||||
|
|
||||||
|
param(
|
||||||
|
[switch]$NoRbxDom
|
||||||
|
)
|
||||||
|
|
||||||
|
$RbxDom = -not $NoRbxDom
|
||||||
|
|
||||||
|
$ErrorActionPreference = "Continue"
|
||||||
|
$failures = @()
|
||||||
|
|
||||||
|
function Write-Step($number, $name) {
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "=== Step ${number}: $name ===" -ForegroundColor Cyan
|
||||||
|
}
|
||||||
|
|
||||||
|
function Record-Result($step, $exitCode) {
|
||||||
|
if ($exitCode -ne 0) {
|
||||||
|
$script:failures += $step
|
||||||
|
Write-Host "FAIL" -ForegroundColor Red
|
||||||
|
} else {
|
||||||
|
Write-Host "PASS" -ForegroundColor Green
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# ─── Rojo ────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
Write-Step 1 "Auto-fix Lua Formatting (Stylua)"
|
||||||
|
stylua plugin/src
|
||||||
|
$styluaRojo = $LASTEXITCODE
|
||||||
|
Record-Result "Stylua (Rojo)" $styluaRojo
|
||||||
|
|
||||||
|
Write-Step 2 "Auto-fix Rust Formatting"
|
||||||
|
cargo fmt
|
||||||
|
$rustfmt = $LASTEXITCODE
|
||||||
|
Record-Result "Rustfmt" $rustfmt
|
||||||
|
|
||||||
|
Write-Step 3 "Auto-fix TypeScript Formatting (Prettier)"
|
||||||
|
Push-Location vscode-rojo
|
||||||
|
npx prettier --write "src/**/*.ts"
|
||||||
|
$prettier = $LASTEXITCODE
|
||||||
|
Pop-Location
|
||||||
|
Record-Result "Prettier" $prettier
|
||||||
|
|
||||||
|
Write-Step 4 "Compile TypeScript Extension (vscode-rojo)"
|
||||||
|
Push-Location vscode-rojo
|
||||||
|
npm run compile
|
||||||
|
$tsBuild = $LASTEXITCODE
|
||||||
|
Pop-Location
|
||||||
|
Record-Result "TS Compile (vscode-rojo)" $tsBuild
|
||||||
|
|
||||||
|
Write-Step 5 "Lua Static Analysis (Selene)"
|
||||||
|
selene plugin/src
|
||||||
|
$selene = $LASTEXITCODE
|
||||||
|
Record-Result "Selene" $selene
|
||||||
|
|
||||||
|
Write-Step 6 "Rust Linting (Clippy) - Auto-fix"
|
||||||
|
cargo clippy --fix --allow-dirty --allow-staged 2>&1 | Out-Null
|
||||||
|
Write-Host "Verifying..." -ForegroundColor Yellow
|
||||||
|
cargo clippy --all-targets --all-features 2>&1
|
||||||
|
$clippy = $LASTEXITCODE
|
||||||
|
Record-Result "Clippy" $clippy
|
||||||
|
|
||||||
|
Write-Step 7 "Build Everything"
|
||||||
|
cargo build --locked --all-targets --all-features
|
||||||
|
$build = $LASTEXITCODE
|
||||||
|
Record-Result "Build" $build
|
||||||
|
|
||||||
|
Write-Step 8 "Run ALL Rust Tests"
|
||||||
|
$testOutput = cargo test --locked --all-targets --all-features 2>&1
|
||||||
|
$rustTests = $LASTEXITCODE
|
||||||
|
$testOutput | Write-Host
|
||||||
|
Record-Result "Rust Tests" $rustTests
|
||||||
|
|
||||||
|
Write-Step 9 "Run Roblox Plugin Tests"
|
||||||
|
.\target\debug\atlas.exe build plugin/test-place.project.json -o TestPlace.rbxl
|
||||||
|
if ($LASTEXITCODE -eq 0) {
|
||||||
|
run-in-roblox --script plugin/run-tests.server.lua --place TestPlace.rbxl
|
||||||
|
$pluginTests = $LASTEXITCODE
|
||||||
|
} else {
|
||||||
|
Write-Host "Skipped: build failed" -ForegroundColor Yellow
|
||||||
|
$pluginTests = 1
|
||||||
|
}
|
||||||
|
Record-Result "Plugin Tests" $pluginTests
|
||||||
|
|
||||||
|
# ─── rbx-dom ─────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
if ($RbxDom) {
|
||||||
|
Write-Step 10 "Auto-fix Lua Formatting (Stylua) for rbx_dom_lua"
|
||||||
|
Push-Location rbx-dom
|
||||||
|
stylua rbx_dom_lua/src
|
||||||
|
$styluaRbxDom = $LASTEXITCODE
|
||||||
|
Pop-Location
|
||||||
|
Record-Result "Stylua (rbx_dom_lua)" $styluaRbxDom
|
||||||
|
|
||||||
|
Write-Step 11 "Auto-fix Rust Formatting (rbx-dom)"
|
||||||
|
Push-Location rbx-dom
|
||||||
|
cargo fmt
|
||||||
|
$rustfmtRbxDom = $LASTEXITCODE
|
||||||
|
Pop-Location
|
||||||
|
Record-Result "Rustfmt (rbx-dom)" $rustfmtRbxDom
|
||||||
|
|
||||||
|
Write-Step 12 "Lua Static Analysis (Selene) for rbx_dom_lua"
|
||||||
|
Push-Location rbx-dom
|
||||||
|
selene rbx_dom_lua/src
|
||||||
|
$seleneRbxDom = $LASTEXITCODE
|
||||||
|
Pop-Location
|
||||||
|
Record-Result "Selene (rbx_dom_lua)" $seleneRbxDom
|
||||||
|
|
||||||
|
Write-Step 13 "Rust Linting (Clippy) for rbx-dom - Auto-fix"
|
||||||
|
Push-Location rbx-dom
|
||||||
|
cargo clippy --fix --allow-dirty --allow-staged 2>&1 | Out-Null
|
||||||
|
Write-Host "Verifying..." -ForegroundColor Yellow
|
||||||
|
cargo clippy --all-targets --all-features -- -D warnings 2>&1
|
||||||
|
$clippyRbxDom = $LASTEXITCODE
|
||||||
|
Pop-Location
|
||||||
|
Record-Result "Clippy (rbx-dom)" $clippyRbxDom
|
||||||
|
|
||||||
|
Write-Step 14 "Build rbx-dom"
|
||||||
|
Push-Location rbx-dom
|
||||||
|
cargo build --verbose
|
||||||
|
$buildRbxDom = $LASTEXITCODE
|
||||||
|
cargo build --all-features --verbose
|
||||||
|
$buildRbxDomAll = $LASTEXITCODE
|
||||||
|
Pop-Location
|
||||||
|
Record-Result "Build (rbx-dom)" $buildRbxDom
|
||||||
|
Record-Result "Build (rbx-dom all-features)" $buildRbxDomAll
|
||||||
|
|
||||||
|
Write-Step 15 "Run ALL rbx-dom Rust Tests"
|
||||||
|
Push-Location rbx-dom
|
||||||
|
cargo test --verbose
|
||||||
|
$testsRbxDom = $LASTEXITCODE
|
||||||
|
cargo test --all-features --verbose
|
||||||
|
$testsRbxDomAll = $LASTEXITCODE
|
||||||
|
Pop-Location
|
||||||
|
Record-Result "Tests (rbx-dom)" $testsRbxDom
|
||||||
|
Record-Result "Tests (rbx-dom all-features)" $testsRbxDomAll
|
||||||
|
}
|
||||||
|
|
||||||
|
# ─── Report ──────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "=== CI COMPLETE ===" -ForegroundColor Magenta
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "--- Rojo ---" -ForegroundColor White
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "Formatting:"
|
||||||
|
Write-Host " - Stylua: $(if ($styluaRojo -eq 0) {'PASS'} else {'FAIL'})"
|
||||||
|
Write-Host " - Rustfmt: $(if ($rustfmt -eq 0) {'PASS'} else {'FAIL'})"
|
||||||
|
Write-Host " - Prettier (vscode-rojo): $(if ($prettier -eq 0) {'PASS'} else {'FAIL'})"
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "TS Compile (vscode-rojo): $(if ($tsBuild -eq 0) {'PASS'} else {'FAIL'})"
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "Linting:"
|
||||||
|
Write-Host " - Selene: $(if ($selene -eq 0) {'PASS'} else {'FAIL'})"
|
||||||
|
Write-Host " - Clippy: $(if ($clippy -eq 0) {'PASS'} else {'FAIL'})"
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "Build: $(if ($build -eq 0) {'PASS'} else {'FAIL'})"
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "Tests:"
|
||||||
|
Write-Host " - Rust: $(if ($rustTests -eq 0) {'PASS'} else {'FAIL'})"
|
||||||
|
Write-Host " - Plugin: $(if ($pluginTests -eq 0) {'PASS'} else {'FAIL'})"
|
||||||
|
|
||||||
|
if ($RbxDom) {
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "--- rbx-dom ---" -ForegroundColor White
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "Formatting:"
|
||||||
|
Write-Host " - Stylua (rbx_dom_lua): $(if ($styluaRbxDom -eq 0) {'PASS'} else {'FAIL'})"
|
||||||
|
Write-Host " - Rustfmt: $(if ($rustfmtRbxDom -eq 0) {'PASS'} else {'FAIL'})"
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "Linting:"
|
||||||
|
Write-Host " - Selene (rbx_dom_lua): $(if ($seleneRbxDom -eq 0) {'PASS'} else {'FAIL'})"
|
||||||
|
Write-Host " - Clippy: $(if ($clippyRbxDom -eq 0) {'PASS'} else {'FAIL'})"
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "Build: $(if ($buildRbxDom -eq 0) {'PASS'} else {'FAIL'})"
|
||||||
|
Write-Host "Build (all features): $(if ($buildRbxDomAll -eq 0) {'PASS'} else {'FAIL'})"
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "Tests:"
|
||||||
|
Write-Host " - Rust: $(if ($testsRbxDom -eq 0) {'PASS'} else {'FAIL'})"
|
||||||
|
Write-Host " - Rust (all features): $(if ($testsRbxDomAll -eq 0) {'PASS'} else {'FAIL'})"
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host ""
|
||||||
|
if ($failures.Count -eq 0) {
|
||||||
|
Write-Host "--- Overall: PASS ---" -ForegroundColor Green
|
||||||
|
exit 0
|
||||||
|
} else {
|
||||||
|
Write-Host "--- Overall: FAIL ---" -ForegroundColor Red
|
||||||
|
Write-Host "Failed steps: $($failures -join ', ')" -ForegroundColor Red
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
181
scripts/ci.sh
Normal file
181
scripts/ci.sh
Normal file
|
|
@ -0,0 +1,181 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# Complete CI Pipeline
|
||||||
|
# Usage: bash scripts/ci.sh [--no-rbx-dom]
|
||||||
|
|
||||||
|
set -o pipefail
|
||||||
|
|
||||||
|
RBX_DOM=true
|
||||||
|
for arg in "$@"; do
|
||||||
|
case "$arg" in
|
||||||
|
--no-rbx-dom) RBX_DOM=false ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
failures=()
|
||||||
|
|
||||||
|
step() {
|
||||||
|
echo ""
|
||||||
|
echo "=== Step $1: $2 ==="
|
||||||
|
}
|
||||||
|
|
||||||
|
record() {
|
||||||
|
if [ "$2" -ne 0 ]; then
|
||||||
|
failures+=("$1")
|
||||||
|
echo "FAIL"
|
||||||
|
else
|
||||||
|
echo "PASS"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# ─── Rojo ────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
step 1 "Auto-fix Lua Formatting (Stylua)"
|
||||||
|
stylua plugin/src
|
||||||
|
stylua_rojo=$?
|
||||||
|
record "Stylua (Rojo)" $stylua_rojo
|
||||||
|
|
||||||
|
step 2 "Auto-fix Rust Formatting"
|
||||||
|
cargo fmt
|
||||||
|
rustfmt_exit=$?
|
||||||
|
record "Rustfmt" $rustfmt_exit
|
||||||
|
|
||||||
|
step 3 "Auto-fix TypeScript Formatting (Prettier)"
|
||||||
|
(cd vscode-rojo && npx prettier --write "src/**/*.ts")
|
||||||
|
prettier_exit=$?
|
||||||
|
record "Prettier" $prettier_exit
|
||||||
|
|
||||||
|
step 4 "Compile TypeScript Extension (vscode-rojo)"
|
||||||
|
(cd vscode-rojo && npm run compile)
|
||||||
|
ts_build=$?
|
||||||
|
record "TS Compile (vscode-rojo)" $ts_build
|
||||||
|
|
||||||
|
step 5 "Lua Static Analysis (Selene)"
|
||||||
|
selene plugin/src
|
||||||
|
selene_exit=$?
|
||||||
|
record "Selene" $selene_exit
|
||||||
|
|
||||||
|
step 6 "Rust Linting (Clippy) - Auto-fix"
|
||||||
|
cargo clippy --fix --allow-dirty --allow-staged >/dev/null 2>&1
|
||||||
|
echo "Verifying..."
|
||||||
|
cargo clippy --all-targets --all-features 2>&1
|
||||||
|
clippy_exit=$?
|
||||||
|
record "Clippy" $clippy_exit
|
||||||
|
|
||||||
|
step 7 "Build Everything"
|
||||||
|
cargo build --locked --all-targets --all-features
|
||||||
|
build_exit=$?
|
||||||
|
record "Build" $build_exit
|
||||||
|
|
||||||
|
step 8 "Run ALL Rust Tests"
|
||||||
|
cargo test --locked --all-targets --all-features 2>&1
|
||||||
|
rust_tests=$?
|
||||||
|
record "Rust Tests" $rust_tests
|
||||||
|
|
||||||
|
step 9 "Run Roblox Plugin Tests"
|
||||||
|
./target/debug/atlas build plugin/test-place.project.json -o TestPlace.rbxl
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
run-in-roblox --script plugin/run-tests.server.lua --place TestPlace.rbxl
|
||||||
|
plugin_tests=$?
|
||||||
|
else
|
||||||
|
echo "Skipped: build failed"
|
||||||
|
plugin_tests=1
|
||||||
|
fi
|
||||||
|
record "Plugin Tests" $plugin_tests
|
||||||
|
|
||||||
|
# ─── rbx-dom ─────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
if [ "$RBX_DOM" = true ]; then
|
||||||
|
step 10 "Auto-fix Lua Formatting (Stylua) for rbx_dom_lua"
|
||||||
|
(cd rbx-dom && stylua rbx_dom_lua/src)
|
||||||
|
stylua_rbxdom=$?
|
||||||
|
record "Stylua (rbx_dom_lua)" $stylua_rbxdom
|
||||||
|
|
||||||
|
step 11 "Auto-fix Rust Formatting (rbx-dom)"
|
||||||
|
(cd rbx-dom && cargo fmt)
|
||||||
|
rustfmt_rbxdom=$?
|
||||||
|
record "Rustfmt (rbx-dom)" $rustfmt_rbxdom
|
||||||
|
|
||||||
|
step 12 "Lua Static Analysis (Selene) for rbx_dom_lua"
|
||||||
|
(cd rbx-dom && selene rbx_dom_lua/src)
|
||||||
|
selene_rbxdom=$?
|
||||||
|
record "Selene (rbx_dom_lua)" $selene_rbxdom
|
||||||
|
|
||||||
|
step 13 "Rust Linting (Clippy) for rbx-dom - Auto-fix"
|
||||||
|
(cd rbx-dom && cargo clippy --fix --allow-dirty --allow-staged >/dev/null 2>&1)
|
||||||
|
echo "Verifying..."
|
||||||
|
(cd rbx-dom && cargo clippy --all-targets --all-features -- -D warnings 2>&1)
|
||||||
|
clippy_rbxdom=$?
|
||||||
|
record "Clippy (rbx-dom)" $clippy_rbxdom
|
||||||
|
|
||||||
|
step 14 "Build rbx-dom"
|
||||||
|
(cd rbx-dom && cargo build --verbose)
|
||||||
|
build_rbxdom=$?
|
||||||
|
(cd rbx-dom && cargo build --all-features --verbose)
|
||||||
|
build_rbxdom_all=$?
|
||||||
|
record "Build (rbx-dom)" $build_rbxdom
|
||||||
|
record "Build (rbx-dom all-features)" $build_rbxdom_all
|
||||||
|
|
||||||
|
step 15 "Run ALL rbx-dom Rust Tests"
|
||||||
|
(cd rbx-dom && cargo test --verbose)
|
||||||
|
tests_rbxdom=$?
|
||||||
|
(cd rbx-dom && cargo test --all-features --verbose)
|
||||||
|
tests_rbxdom_all=$?
|
||||||
|
record "Tests (rbx-dom)" $tests_rbxdom
|
||||||
|
record "Tests (rbx-dom all-features)" $tests_rbxdom_all
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ─── Report ──────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
pass_or_fail() { [ "$1" -eq 0 ] && echo "PASS" || echo "FAIL"; }
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "=== CI COMPLETE ==="
|
||||||
|
echo ""
|
||||||
|
echo "--- Rojo ---"
|
||||||
|
echo ""
|
||||||
|
echo "Formatting:"
|
||||||
|
echo " - Stylua: $(pass_or_fail $stylua_rojo)"
|
||||||
|
echo " - Rustfmt: $(pass_or_fail $rustfmt_exit)"
|
||||||
|
echo " - Prettier (vscode-rojo): $(pass_or_fail $prettier_exit)"
|
||||||
|
echo ""
|
||||||
|
echo "TS Compile (vscode-rojo): $(pass_or_fail $ts_build)"
|
||||||
|
echo ""
|
||||||
|
echo "Linting:"
|
||||||
|
echo " - Selene: $(pass_or_fail $selene_exit)"
|
||||||
|
echo " - Clippy: $(pass_or_fail $clippy_exit)"
|
||||||
|
echo ""
|
||||||
|
echo "Build: $(pass_or_fail $build_exit)"
|
||||||
|
echo ""
|
||||||
|
echo "Tests:"
|
||||||
|
echo " - Rust: $(pass_or_fail $rust_tests)"
|
||||||
|
echo " - Plugin: $(pass_or_fail $plugin_tests)"
|
||||||
|
|
||||||
|
if [ "$RBX_DOM" = true ]; then
|
||||||
|
echo ""
|
||||||
|
echo "--- rbx-dom ---"
|
||||||
|
echo ""
|
||||||
|
echo "Formatting:"
|
||||||
|
echo " - Stylua (rbx_dom_lua): $(pass_or_fail $stylua_rbxdom)"
|
||||||
|
echo " - Rustfmt: $(pass_or_fail $rustfmt_rbxdom)"
|
||||||
|
echo ""
|
||||||
|
echo "Linting:"
|
||||||
|
echo " - Selene (rbx_dom_lua): $(pass_or_fail $selene_rbxdom)"
|
||||||
|
echo " - Clippy: $(pass_or_fail $clippy_rbxdom)"
|
||||||
|
echo ""
|
||||||
|
echo "Build: $(pass_or_fail $build_rbxdom)"
|
||||||
|
echo "Build (all features): $(pass_or_fail $build_rbxdom_all)"
|
||||||
|
echo ""
|
||||||
|
echo "Tests:"
|
||||||
|
echo " - Rust: $(pass_or_fail $tests_rbxdom)"
|
||||||
|
echo " - Rust (all features): $(pass_or_fail $tests_rbxdom_all)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
if [ ${#failures[@]} -eq 0 ]; then
|
||||||
|
echo "--- Overall: PASS ---"
|
||||||
|
exit 0
|
||||||
|
else
|
||||||
|
echo "--- Overall: FAIL ---"
|
||||||
|
echo "Failed steps: $(IFS=', '; echo "${failures[*]}")"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
@ -296,11 +296,7 @@ impl JobThreadContext {
|
||||||
match crate::json::to_vec_pretty_sorted(&serde_json::Value::Object(obj)) {
|
match crate::json::to_vec_pretty_sorted(&serde_json::Value::Object(obj)) {
|
||||||
Ok(content) => {
|
Ok(content) => {
|
||||||
if let Err(err) = fs::write(meta_path, &content) {
|
if let Err(err) = fs::write(meta_path, &content) {
|
||||||
log::error!(
|
log::error!("Failed to write meta file {}: {}", meta_path.display(), err);
|
||||||
"Failed to write meta file {}: {}",
|
|
||||||
meta_path.display(),
|
|
||||||
err
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
|
|
@ -773,8 +769,8 @@ impl JobThreadContext {
|
||||||
self.unsuppress_path(&new_meta);
|
self.unsuppress_path(&new_meta);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let init_meta = new_dir_path
|
let init_meta =
|
||||||
.join("init.meta.json5");
|
new_dir_path.join("init.meta.json5");
|
||||||
if slugified_new_name != *new_name {
|
if slugified_new_name != *new_name {
|
||||||
// New name needs slugification — write name field
|
// New name needs slugification — write name field
|
||||||
self.upsert_meta_name_field(
|
self.upsert_meta_name_field(
|
||||||
|
|
|
||||||
|
|
@ -109,11 +109,20 @@ pub fn syncback_csv<'sync>(
|
||||||
|
|
||||||
if !meta.is_empty() {
|
if !meta.is_empty() {
|
||||||
let parent = snapshot.path.parent_err()?;
|
let parent = snapshot.path.parent_err()?;
|
||||||
let instance_name = &new_inst.name;
|
let meta_name = snapshot
|
||||||
let meta_name = if name_needs_slugify(instance_name) {
|
.path
|
||||||
slugify_name(instance_name)
|
.file_stem()
|
||||||
|
.and_then(|s| s.to_str())
|
||||||
|
.unwrap_or("");
|
||||||
|
let meta_name = if meta_name.is_empty() {
|
||||||
|
let instance_name = &new_inst.name;
|
||||||
|
if name_needs_slugify(instance_name) {
|
||||||
|
slugify_name(instance_name)
|
||||||
|
} else {
|
||||||
|
instance_name.clone()
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
instance_name.clone()
|
meta_name.to_string()
|
||||||
};
|
};
|
||||||
fs_snapshot.add_file(
|
fs_snapshot.add_file(
|
||||||
parent.join(format!("{}.meta.json5", meta_name)),
|
parent.join(format!("{}.meta.json5", meta_name)),
|
||||||
|
|
|
||||||
|
|
@ -186,19 +186,22 @@ pub fn syncback_dir_no_meta<'sync>(
|
||||||
old_child_map.insert(inst.name(), inst);
|
old_child_map.insert(inst.name(), inst);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pre-seed taken_names from old children's slugified instance names.
|
// Pre-seed taken_names from old children's slugified instance names
|
||||||
// This ensures new-only children that happen to slugify to the same
|
// so that new-only children correctly dedup against existing siblings
|
||||||
// bare slug as an existing sibling will be deduplicated (e.g., new
|
// (e.g., new "A/B" → slug "A_B" won't collide with existing "A_B").
|
||||||
// "A/B" → slug "A_B" won't collide with existing "A_B").
|
// Only in incremental mode: in clean mode, old_ref is forced to None
|
||||||
// Matches the approach used in api.rs (lines 902-920).
|
// for all children, so every child is treated as new and pre-seeding
|
||||||
for (_, inst) in &old_child_map {
|
// would cause false collisions (spurious ~1 suffixes).
|
||||||
let name = inst.name();
|
if snapshot.data.is_incremental() {
|
||||||
let slug = if name_needs_slugify(name) {
|
for inst in old_child_map.values() {
|
||||||
slugify_name(name).to_lowercase()
|
let name = inst.name();
|
||||||
} else {
|
let slug = if name_needs_slugify(name) {
|
||||||
name.to_lowercase()
|
slugify_name(name).to_lowercase()
|
||||||
};
|
} else {
|
||||||
taken_names.insert(slug);
|
name.to_lowercase()
|
||||||
|
};
|
||||||
|
taken_names.insert(slug);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for new_child_ref in new_inst.children() {
|
for new_child_ref in new_inst.children() {
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,10 @@ use crate::{
|
||||||
PathIgnoreRule, SyncRule,
|
PathIgnoreRule, SyncRule,
|
||||||
},
|
},
|
||||||
snapshot_middleware::Middleware,
|
snapshot_middleware::Middleware,
|
||||||
syncback::{filter_properties, inst_path, FsSnapshot, SyncbackReturn, SyncbackSnapshot},
|
syncback::{
|
||||||
|
filter_properties, inst_path, name_needs_slugify, slugify_name, FsSnapshot, SyncbackReturn,
|
||||||
|
SyncbackSnapshot,
|
||||||
|
},
|
||||||
variant_eq::variant_eq,
|
variant_eq::variant_eq,
|
||||||
RojoRef,
|
RojoRef,
|
||||||
};
|
};
|
||||||
|
|
@ -655,6 +658,27 @@ pub fn syncback_project<'sync>(
|
||||||
std::collections::HashSet<String>,
|
std::collections::HashSet<String>,
|
||||||
> = std::collections::HashMap::new();
|
> = std::collections::HashMap::new();
|
||||||
|
|
||||||
|
// Pre-seed taken_names from old children's slugified instance names
|
||||||
|
// so that new-only children correctly dedup against existing siblings
|
||||||
|
// (e.g., new "utils" won't collide with existing "Utils.luau").
|
||||||
|
// Only in incremental mode: in clean mode, old_ref is forced to None
|
||||||
|
// for all children, so every child is treated as new and pre-seeding
|
||||||
|
// would cause false collisions (spurious ~1 suffixes).
|
||||||
|
if snapshot.data.is_incremental() {
|
||||||
|
if let Some(parent_path) = ref_to_path_map.get(&new_inst.referent()) {
|
||||||
|
let taken = taken_names_per_dir.entry(parent_path.clone()).or_default();
|
||||||
|
for old_child in old_child_map.values() {
|
||||||
|
let name = old_child.name();
|
||||||
|
let slug = if name_needs_slugify(name) {
|
||||||
|
slugify_name(name).to_lowercase()
|
||||||
|
} else {
|
||||||
|
name.to_lowercase()
|
||||||
|
};
|
||||||
|
taken.insert(slug);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (name, new_child) in new_child_map.drain() {
|
for (name, new_child) in new_child_map.drain() {
|
||||||
// Skip instances of ignored classes
|
// Skip instances of ignored classes
|
||||||
if snapshot.should_ignore_class(&new_child.class) {
|
if snapshot.should_ignore_class(&new_child.class) {
|
||||||
|
|
|
||||||
|
|
@ -58,11 +58,20 @@ pub fn syncback_txt<'sync>(
|
||||||
|
|
||||||
if !meta.is_empty() {
|
if !meta.is_empty() {
|
||||||
let parent = snapshot.path.parent_err()?;
|
let parent = snapshot.path.parent_err()?;
|
||||||
let instance_name = &new_inst.name;
|
let meta_name = snapshot
|
||||||
let meta_name = if name_needs_slugify(instance_name) {
|
.path
|
||||||
slugify_name(instance_name)
|
.file_stem()
|
||||||
|
.and_then(|s| s.to_str())
|
||||||
|
.unwrap_or("");
|
||||||
|
let meta_name = if meta_name.is_empty() {
|
||||||
|
let instance_name = &new_inst.name;
|
||||||
|
if name_needs_slugify(instance_name) {
|
||||||
|
slugify_name(instance_name)
|
||||||
|
} else {
|
||||||
|
instance_name.clone()
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
instance_name.clone()
|
meta_name.to_string()
|
||||||
};
|
};
|
||||||
fs_snapshot.add_file(
|
fs_snapshot.add_file(
|
||||||
parent.join(format!("{}.meta.json5", meta_name)),
|
parent.join(format!("{}.meta.json5", meta_name)),
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue