Add IDE Extensions documentation for VS Code integration #29
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: CI | |
| on: | |
| push: | |
| branches: ['**'] | |
| pull_request: | |
| branches: [main] | |
| workflow_dispatch: | |
| inputs: | |
| build_artifacts: | |
| description: 'Build binaries and extensions (skips tier 2)' | |
| required: false | |
| type: boolean | |
| default: false | |
| dry_run: | |
| description: 'Dry run (skip release creation)' | |
| required: false | |
| type: boolean | |
| default: true | |
| env: | |
| CARGO_TERM_COLOR: always | |
| jobs: | |
| # ============================================================ | |
| # Tier 1: Fast checks — runs on every push to any branch | |
| # ============================================================ | |
| tier1-checks: | |
| name: Tier 1 Fast Checks | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Install Rust toolchain | |
| uses: dtolnay/rust-toolchain@stable | |
| with: | |
| components: rustfmt, clippy | |
| - name: Cache cargo registry | |
| uses: actions/cache@v4 | |
| with: | |
| path: ~/.cargo/registry | |
| key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }} | |
| restore-keys: | | |
| ${{ runner.os }}-cargo-registry- | |
| - name: Cache cargo index | |
| uses: actions/cache@v4 | |
| with: | |
| path: ~/.cargo/git | |
| key: ${{ runner.os }}-cargo-index-${{ hashFiles('**/Cargo.lock') }} | |
| restore-keys: | | |
| ${{ runner.os }}-cargo-index- | |
| - name: Cache cargo build | |
| uses: actions/cache@v4 | |
| with: | |
| path: target | |
| key: ${{ runner.os }}-cargo-build-${{ hashFiles('**/Cargo.lock') }} | |
| restore-keys: | | |
| ${{ runner.os }}-cargo-build- | |
| - name: Format check | |
| run: cargo fmt --check | |
| - name: Linting | |
| run: cargo clippy --all-targets --all-features -- -D warnings | |
| - name: Unit tests | |
| run: cargo test --lib | |
| # ============================================================ | |
| # Tier 2: Heavy jobs — only on PRs to main or tag pushes | |
| # ============================================================ | |
| integration-tests: | |
| name: Tests (${{ matrix.os }}) | |
| needs: tier1-checks | |
| if: github.event_name == 'pull_request' || startsWith(github.ref, 'refs/tags/v') || github.event_name == 'workflow_dispatch' | |
| runs-on: ${{ matrix.os }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| os: [ubuntu-latest, macos-latest, windows-latest] | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Install Rust toolchain | |
| uses: dtolnay/rust-toolchain@stable | |
| - name: Cache cargo registry | |
| uses: actions/cache@v4 | |
| with: | |
| path: ~/.cargo/registry | |
| key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }} | |
| restore-keys: | | |
| ${{ runner.os }}-cargo-registry- | |
| - name: Cache cargo index | |
| uses: actions/cache@v4 | |
| with: | |
| path: ~/.cargo/git | |
| key: ${{ runner.os }}-cargo-git-${{ hashFiles('**/Cargo.lock') }} | |
| restore-keys: | | |
| ${{ runner.os }}-cargo-git- | |
| - name: Cache cargo build | |
| uses: actions/cache@v4 | |
| with: | |
| path: target | |
| key: ${{ runner.os }}-cargo-build-target-${{ hashFiles('**/Cargo.lock') }} | |
| restore-keys: | | |
| ${{ runner.os }}-cargo-build-target- | |
| - name: Build | |
| run: cargo build --all-features | |
| - name: Run unit tests | |
| run: cargo test --all-features --lib | |
| - name: Run binary tests | |
| run: cargo test --all-features --bin writ | |
| - name: Run integration tests (writ-lexer) | |
| run: cargo test --all-features -p writ-lexer --tests | |
| - name: Run integration tests (writ-parser) | |
| run: cargo test --all-features -p writ-parser --tests | |
| - name: Run integration tests (writ-types) | |
| run: cargo test --all-features -p writ-types --tests | |
| - name: Run integration tests (writ-compiler) | |
| run: cargo test --all-features -p writ-compiler --tests | |
| - name: Run integration tests (writ-vm) | |
| run: cargo test --all-features -p writ-vm --tests | |
| - name: Run integration tests (writ-stdlib) | |
| run: cargo test --all-features -p writ-stdlib --tests | |
| - name: Run integration tests (writ-codegen) | |
| run: cargo test --all-features -p writ-codegen --tests | |
| - name: Run integration tests (writ-lsp) | |
| run: cargo test --all-features -p writ-lsp --tests | |
| - name: Run root integration tests | |
| run: cargo test --all-features --tests | |
| security-scan: | |
| name: Security Scan | |
| needs: tier1-checks | |
| if: github.event_name == 'pull_request' || startsWith(github.ref, 'refs/tags/v') || github.event_name == 'workflow_dispatch' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Run Semgrep | |
| uses: returntocorp/semgrep-action@v1 | |
| with: | |
| config: >- | |
| p/security-audit | |
| p/owasp-top-ten | |
| p/r2c-security-audit | |
| p/rust | |
| coverage: | |
| name: Code Coverage | |
| needs: tier1-checks | |
| if: github.event_name == 'pull_request' || startsWith(github.ref, 'refs/tags/v') || github.event_name == 'workflow_dispatch' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Install Rust toolchain | |
| uses: dtolnay/rust-toolchain@stable | |
| - name: Cache cargo registry | |
| uses: actions/cache@v4 | |
| with: | |
| path: ~/.cargo/registry | |
| key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }} | |
| restore-keys: | | |
| ${{ runner.os }}-cargo-registry- | |
| - name: Cache cargo index | |
| uses: actions/cache@v4 | |
| with: | |
| path: ~/.cargo/git | |
| key: ${{ runner.os }}-cargo-index-${{ hashFiles('**/Cargo.lock') }} | |
| restore-keys: | | |
| ${{ runner.os }}-cargo-index- | |
| - name: Cache cargo build | |
| uses: actions/cache@v4 | |
| with: | |
| path: target | |
| key: ${{ runner.os }}-cargo-build-${{ hashFiles('**/Cargo.lock') }} | |
| restore-keys: | | |
| ${{ runner.os }}-cargo-build- | |
| - name: Install tarpaulin | |
| run: cargo install cargo-tarpaulin | |
| - name: Generate coverage | |
| run: cargo tarpaulin --all-features --features debug-hooks --out xml --out lcov --verbose | |
| - name: Extract coverage percentage | |
| id: coverage | |
| run: | | |
| COVERAGE=$(grep -o 'line-rate="[0-9.]*"' tarpaulin-report.xml | head -1 | cut -d'"' -f2 || echo "0") | |
| COVERAGE_PERCENT=$(echo "$COVERAGE * 100" | bc | xargs printf "%.1f") | |
| echo "percentage=$COVERAGE_PERCENT" >> $GITHUB_OUTPUT | |
| echo "Coverage: $COVERAGE_PERCENT%" | |
| - name: Upload coverage to Codecov | |
| uses: codecov/codecov-action@v4 | |
| with: | |
| files: ./tarpaulin-report.xml,./lcov.info | |
| fail_ci_if_error: false | |
| token: ${{ secrets.CODECOV_TOKEN }} | |
| verbose: true | |
| - name: Upload coverage artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: coverage-report | |
| path: | | |
| tarpaulin-report.xml | |
| lcov.info | |
| - name: Check coverage threshold | |
| run: | | |
| COVERAGE="${{ steps.coverage.outputs.percentage }}" | |
| echo "Line coverage: ${COVERAGE}%" | |
| if (( $(echo "$COVERAGE < 70.0" | bc -l) )); then | |
| echo "WARNING: Coverage ${COVERAGE}% is below threshold of 70%" | |
| fi | |
| echo "Coverage ${COVERAGE}% meets threshold of 70%" | |
| benchmarks: | |
| name: Benchmarks | |
| needs: tier1-checks | |
| if: github.event_name == 'pull_request' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Install Rust toolchain | |
| uses: dtolnay/rust-toolchain@stable | |
| - name: Cache cargo | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| ~/.cargo/registry | |
| ~/.cargo/git | |
| target | |
| key: ${{ runner.os }}-cargo-bench-${{ hashFiles('**/Cargo.lock') }} | |
| restore-keys: | | |
| ${{ runner.os }}-cargo-bench- | |
| - name: Run benchmarks | |
| run: cargo bench --bench lexer --bench parser --bench compiler --bench vm --bench pipeline | |
| - name: Upload benchmark results | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: benchmark-results | |
| path: target/criterion/ | |
| # ============================================================ | |
| # Tier 3: Build binaries — only after all tier 2 jobs pass | |
| # ============================================================ | |
| build-binaries: | |
| name: Build (${{ matrix.platform }}-${{ matrix.arch }}) | |
| needs: [tier1-checks, integration-tests, security-scan, coverage] | |
| if: | | |
| always() && ( | |
| (github.event_name == 'workflow_dispatch' && github.event.inputs.build_artifacts == 'true' && needs.tier1-checks.result == 'success') || | |
| (needs.integration-tests.result == 'success' && needs.security-scan.result == 'success' && needs.coverage.result == 'success') | |
| ) | |
| runs-on: ${{ matrix.os }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - os: ubuntu-latest | |
| platform: linux | |
| target: x86_64-unknown-linux-gnu | |
| arch: x86_64 | |
| use_cross: false | |
| - os: ubuntu-latest | |
| platform: linux | |
| target: aarch64-unknown-linux-gnu | |
| arch: aarch64 | |
| use_cross: true | |
| - os: macos-latest | |
| platform: macos | |
| target: x86_64-apple-darwin | |
| arch: x86_64 | |
| use_cross: false | |
| - os: macos-latest | |
| platform: macos | |
| target: aarch64-apple-darwin | |
| arch: aarch64 | |
| use_cross: false | |
| - os: windows-latest | |
| platform: windows | |
| target: x86_64-pc-windows-msvc | |
| arch: x86_64 | |
| use_cross: false | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Install Rust toolchain | |
| uses: dtolnay/rust-toolchain@stable | |
| - name: Add target | |
| run: rustup target add ${{ matrix.target }} | |
| - name: Install cross (for ARM builds) | |
| if: matrix.use_cross | |
| run: cargo install cross --git https://github.com/cross-rs/cross | |
| - name: Build binaries | |
| shell: bash | |
| env: | |
| USE_CROSS: ${{ matrix.use_cross }} | |
| BUILD_TARGET: ${{ matrix.target }} | |
| run: | | |
| if [ "$USE_CROSS" = "true" ]; then | |
| cross build --release --target "$BUILD_TARGET" | |
| else | |
| cargo build --release --target "$BUILD_TARGET" | |
| fi | |
| - name: Package binaries (Unix) | |
| if: matrix.platform != 'windows' | |
| shell: bash | |
| run: | | |
| mkdir -p release-binaries | |
| cp "target/${{ matrix.target }}/release/writ" release-binaries/ | |
| cp "target/${{ matrix.target }}/release/writ-lsp" release-binaries/ | |
| tar czf "writ-${{ matrix.platform }}-${{ matrix.arch }}.tar.gz" -C release-binaries writ writ-lsp | |
| - name: Package binaries (Windows) | |
| if: matrix.platform == 'windows' | |
| shell: bash | |
| run: | | |
| mkdir -p release-binaries | |
| cp "target/${{ matrix.target }}/release/writ.exe" release-binaries/ | |
| cp "target/${{ matrix.target }}/release/writ-lsp.exe" release-binaries/ | |
| cd release-binaries | |
| 7z a -tzip "../writ-${{ matrix.platform }}-${{ matrix.arch }}.zip" writ.exe writ-lsp.exe | |
| - name: Upload binary artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: binary-${{ matrix.platform }}-${{ matrix.arch }} | |
| path: writ-${{ matrix.platform }}-${{ matrix.arch }}.* | |
| build-vscode-extension: | |
| name: VSCode Extension (${{ matrix.platform }}) | |
| needs: build-binaries | |
| runs-on: ${{ matrix.os }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - os: ubuntu-latest | |
| platform: linux | |
| arch: x86_64 | |
| lsp_binary: writ-lsp | |
| - os: macos-latest | |
| platform: macos | |
| arch: aarch64 | |
| lsp_binary: writ-lsp | |
| - os: windows-latest | |
| platform: windows | |
| arch: x86_64 | |
| lsp_binary: writ-lsp.exe | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Download LSP binary | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: binary-${{ matrix.platform }}-${{ matrix.arch }} | |
| path: lsp-artifact | |
| - name: Extract LSP binary | |
| shell: bash | |
| run: | | |
| mkdir -p extensions/vscode-writ/bin | |
| if [ "${{ matrix.platform }}" = "windows" ]; then | |
| cd lsp-artifact && 7z x "writ-${{ matrix.platform }}-${{ matrix.arch }}.zip" -o../lsp-extracted | |
| else | |
| tar xzf "lsp-artifact/writ-${{ matrix.platform }}-${{ matrix.arch }}.tar.gz" -C lsp-extracted || mkdir -p lsp-extracted && tar xzf "lsp-artifact/writ-${{ matrix.platform }}-${{ matrix.arch }}.tar.gz" -C lsp-extracted | |
| fi | |
| cp "lsp-extracted/${{ matrix.lsp_binary }}" "extensions/vscode-writ/bin/" | |
| chmod +x "extensions/vscode-writ/bin/${{ matrix.lsp_binary }}" 2>/dev/null || true | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: 20 | |
| - name: Install pnpm | |
| uses: pnpm/action-setup@v4 | |
| with: | |
| version: latest | |
| - name: Install dependencies | |
| working-directory: extensions/vscode-writ | |
| run: pnpm install | |
| - name: Build extension | |
| working-directory: extensions/vscode-writ | |
| run: pnpm run build | |
| - name: Package VSIX | |
| working-directory: extensions/vscode-writ | |
| run: pnpm run package | |
| - name: Upload VSIX artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: vsix-${{ matrix.platform }}-${{ matrix.arch }} | |
| path: extensions/vscode-writ/*.vsix | |
| build-jetbrains-extension: | |
| name: JetBrains Extension | |
| needs: [tier1-checks, integration-tests] | |
| if: | | |
| always() && ( | |
| (github.event_name == 'workflow_dispatch' && github.event.inputs.build_artifacts == 'true' && needs.tier1-checks.result == 'success') || | |
| needs.integration-tests.result == 'success' | |
| ) | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Setup Java | |
| uses: actions/setup-java@v4 | |
| with: | |
| distribution: temurin | |
| java-version: 17 | |
| - name: Build plugin | |
| working-directory: extensions/jetbrains-writ | |
| run: ./gradlew buildPlugin | |
| - name: Upload plugin artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: jetbrains-plugin | |
| path: extensions/jetbrains-writ/build/distributions/*.zip | |
| # ============================================================ | |
| # Release: Only on version tags or manual dispatch (non-dry-run) | |
| # ============================================================ | |
| create-release: | |
| name: Create GitHub Release | |
| needs: [build-binaries, build-vscode-extension, build-jetbrains-extension] | |
| runs-on: ubuntu-latest | |
| if: startsWith(github.ref, 'refs/tags/v') || (github.event_name == 'workflow_dispatch' && github.event.inputs.dry_run == 'false') | |
| permissions: | |
| contents: write | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Download all binary artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| path: artifacts | |
| - name: Prepare release assets | |
| run: | | |
| mkdir -p release-assets | |
| find artifacts/binary-* -type f \( -name "*.tar.gz" -o -name "*.zip" \) -exec mv {} release-assets/ \; | |
| find artifacts/vsix-* -type f -name "*.vsix" -exec mv {} release-assets/ \; | |
| find artifacts/jetbrains-plugin -type f -name "*.zip" -exec cp {} release-assets/writ-jetbrains-plugin.zip \; 2>/dev/null || true | |
| find artifacts/coverage-report -type f \( -name "*.xml" -o -name "*.info" \) -exec cp {} release-assets/ \; 2>/dev/null || true | |
| cd release-assets | |
| sha256sum * > SHA256SUMS 2>/dev/null || shasum -a 256 * > SHA256SUMS | |
| - name: Extract version from tag | |
| id: version | |
| run: | | |
| if [ "${{ github.event_name }}" = "push" ]; then | |
| VERSION=${GITHUB_REF#refs/tags/v} | |
| else | |
| VERSION=$(grep '^version = ' Cargo.toml | head -1 | sed 's/.*"\(.*\)".*/\1/') | |
| fi | |
| echo "version=$VERSION" >> $GITHUB_OUTPUT | |
| - name: Create Release | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| name: Release v${{ steps.version.outputs.version }} | |
| tag_name: v${{ steps.version.outputs.version }} | |
| draft: false | |
| prerelease: ${{ contains(steps.version.outputs.version, '-') }} | |
| generate_release_notes: true | |
| files: | | |
| release-assets/* | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} |