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
75 changes: 75 additions & 0 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
name: Docs

on:
push:
branches:
- master
paths:
- 'docs/**'
- 'mkdocs.yml'
- 'docs/requirements.txt'
- '.github/workflows/docs.yml'
pull_request:
paths:
- 'docs/**'
- 'mkdocs.yml'
- 'docs/requirements.txt'
- '.github/workflows/docs.yml'
workflow_dispatch:

permissions:
contents: read

# Only one Pages deployment can run at a time. Cancelling a queued
# deployment in favour of a newer one is fine; cancelling one already
# in flight would orphan a partial deploy, so we let it finish.
concurrency:
group: pages
cancel-in-progress: false

jobs:
build:
name: Build site
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@v6

- uses: actions/setup-python@v6
with:
python-version: '3.x'
cache: pip
cache-dependency-path: docs/requirements.txt

- name: Install MkDocs and theme
run: pip install -r docs/requirements.txt

- name: Strict build (fails on warnings)
run: mkdocs build --strict

# Upload the generated site so the deploy job (or anyone debugging
# a PR) can grab it. Uses the dedicated Pages artifact format.
- name: Upload Pages artifact
if: github.event_name == 'push' && github.ref == 'refs/heads/master'
uses: actions/upload-pages-artifact@v5
with:
path: site

deploy:
name: Deploy to GitHub Pages
needs: build
if: github.event_name == 'push' && github.ref == 'refs/heads/master'
runs-on: ubuntu-latest
# `actions/deploy-pages` needs `pages: write` to publish and
# `id-token: write` for OIDC verification of the artifact.
permissions:
pages: write
id-token: write
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v5
126 changes: 126 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
name: Release

on:
release:
types: [published, prereleased]

permissions:
contents: read

env:
CARGO_TERM_COLOR: always
PLUGIN_NAME: env_samp

jobs:
build-release:
runs-on: ubuntu-latest
# Needs write access to attach .so/.dll/.inc to the GitHub release
# and to amend the release body with the auto-generated notes.
permissions:
contents: write

steps:
- uses: actions/checkout@v6

- name: Verify Cargo.toml version matches release tag
run: |
TAG_VERSION="${GITHUB_REF_NAME#v}"
CARGO_VERSION="$(grep -m1 '^version' Cargo.toml | sed 's/.*= *"\(.*\)"/\1/')"
echo "Release tag: ${GITHUB_REF_NAME} (version=${TAG_VERSION})"
echo "Cargo.toml: ${CARGO_VERSION}"
if [ "${TAG_VERSION}" != "${CARGO_VERSION}" ]; then
echo "::error::Release tag (${GITHUB_REF_NAME}) does not match Cargo.toml version (${CARGO_VERSION})."
echo "::error::Bump Cargo.toml to ${TAG_VERSION} (or retag) and try again."
exit 1
fi

- name: Install Rust
uses: dtolnay/rust-toolchain@stable
with:
targets: i686-unknown-linux-gnu,i686-pc-windows-msvc

- name: Install cross-compilation tools
run: |
sudo apt-get update
sudo apt-get install -y gcc-multilib g++-multilib clang llvm

- name: Install cargo-xwin
run: cargo install cargo-xwin --locked

- name: Cache cargo
uses: actions/cache@v5
with:
path: |
~/.cargo/registry
~/.cargo/git
~/.cache/cargo-xwin
target
key: ${{ runner.os }}-cargo-release-${{ hashFiles('**/Cargo.lock') }}

- name: Build Linux (i686, SA-MP + native OMP)
run: cargo build --release --target i686-unknown-linux-gnu

- name: Build Windows (i686 MSVC, SA-MP + native OMP)
run: cargo xwin build --xwin-arch x86 --release --target i686-pc-windows-msvc

- name: Stage release artifacts
run: |
mkdir -p dist
cp "target/i686-unknown-linux-gnu/release/lib${PLUGIN_NAME}.so" "dist/${PLUGIN_NAME}.so"
cp "target/i686-pc-windows-msvc/release/${PLUGIN_NAME}.dll" "dist/${PLUGIN_NAME}.dll"
cp "include/${PLUGIN_NAME}.inc" "dist/${PLUGIN_NAME}.inc"

- name: Generate release notes
run: |
SDK_VERSION="$(grep -oP 'tag\s*=\s*"\Kv[0-9][^"]*' Cargo.toml | head -n1)"
PLUGIN_VERSION="$(grep -m1 '^version' Cargo.toml | sed 's/.*= *"\(.*\)"/\1/')"

# Extract the section for the current version from CHANGELOG.md.
# Format: "## [X.Y.Z] — yyyy/mm/dd" up to the next "## " heading.
CHANGELOG_SECTION=""
if [ -f CHANGELOG.md ]; then
CHANGELOG_SECTION="$(awk -v ver="${PLUGIN_VERSION}" '
$0 ~ "^## \\[" ver "\\]" { capture = 1; next }
capture && /^## / { exit }
capture { print }
' CHANGELOG.md)"
fi

cat > release_body.md <<EOF
## ${PLUGIN_NAME} v${PLUGIN_VERSION}

Built on top of [rust-samp ${SDK_VERSION}](https://github.com/NullSablex/rust-samp/releases/tag/${SDK_VERSION}).

### Artifacts
- \`${PLUGIN_NAME}.so\` — Linux i686 (\`i686-unknown-linux-gnu\`).
- \`${PLUGIN_NAME}.dll\` — Windows i686 (\`i686-pc-windows-msvc\`).
- \`${PLUGIN_NAME}.inc\` — Pawn include, identical file for SA-MP and Open Multiplayer.

### Compatibility
The binaries are **universal**: they run on SA-MP and on Open Multiplayer, on Linux and Windows alike.

- **SA-MP**: drop the binary into \`plugins/\` and register it under \`plugins=\` in \`server.cfg\`.
- **Open Multiplayer (native mode, recommended)**: drop the binary into the server's \`components/\` folder. It is loaded automatically via \`ComponentEntryPoint\`, with access to \`ICore\`, \`ITimersComponent\` and the remaining native APIs. No \`config.json\` entry is required.
- **Open Multiplayer (legacy mode)**: drop the binary into \`plugins/\` and declare it under \`pawn.legacy_plugins\` in \`config.json\`. Same binary, no extra build flags.
EOF

if [ -n "${CHANGELOG_SECTION}" ]; then
{
echo ""
echo "### Changelog"
echo ""
echo "${CHANGELOG_SECTION}"
} >> release_body.md
fi

cat release_body.md

- name: Upload release assets
uses: softprops/action-gh-release@v3
with:
body_path: release_body.md
append_body: true
files: |
dist/${{ env.PLUGIN_NAME }}.so
dist/${{ env.PLUGIN_NAME }}.dll
dist/${{ env.PLUGIN_NAME }}.inc
125 changes: 92 additions & 33 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,41 +9,100 @@ on:
env:
CARGO_TERM_COLOR: always

# Default: read-only. Each job opts in to extra scopes when needed.
permissions:
contents: read

jobs:
build:
name: Build, test, clippy
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@v6

- name: Install Rust (stable + clippy)
uses: dtolnay/rust-toolchain@stable
with:
targets: i686-unknown-linux-gnu
components: clippy

- name: Install cross-compilation tools
run: |
sudo apt-get update
sudo apt-get install -y gcc-multilib g++-multilib

- uses: Swatinem/rust-cache@v2

- name: Build
run: cargo build --target i686-unknown-linux-gnu --verbose

- name: Test
run: cargo test --target i686-unknown-linux-gnu --verbose

- name: Clippy
run: cargo clippy --target i686-unknown-linux-gnu --all-targets -- -D warnings

fmt:
name: Rustfmt
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@v4

- name: Install Rust
uses: dtolnay/rust-toolchain@stable
with:
targets: i686-unknown-linux-gnu

- name: Cache cargo registry
uses: actions/cache@v3
with:
path: ~/.cargo/registry
key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }}

- name: Cache cargo index
uses: actions/cache@v3
with:
path: ~/.cargo/git
key: ${{ runner.os }}-cargo-git-${{ hashFiles('**/Cargo.lock') }}

- name: Cache cargo build
uses: actions/cache@v3
with:
path: target
key: ${{ runner.os }}-cargo-build-target-${{ hashFiles('**/Cargo.lock') }}

- name: Build
run: cargo build --verbose

- name: Run tests
run: cargo test --verbose

- name: Run clippy
run: cargo clippy --all-targets -- -D warnings
- uses: actions/checkout@v6
- name: Install Rust (stable + rustfmt)
uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt
- run: cargo fmt --all -- --check

audit:
name: Security audit
runs-on: ubuntu-latest
# rustsec/audit-check opens issues for new advisories and posts
# review comments on PRs that introduce vulnerable dependencies.
permissions:
contents: read
issues: write
pull-requests: write
checks: write
steps:
- uses: actions/checkout@v6
- uses: rustsec/audit-check@v2
with:
token: ${{ secrets.GITHUB_TOKEN }}

coverage:
name: Coverage (llvm-cov)
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@v6

- name: Install Rust (stable + llvm-tools)
uses: dtolnay/rust-toolchain@stable
with:
targets: i686-unknown-linux-gnu
components: llvm-tools-preview

- name: Install cross-compilation tools
run: |
sudo apt-get update
sudo apt-get install -y gcc-multilib g++-multilib

- uses: Swatinem/rust-cache@v2

- name: Install cargo-llvm-cov
uses: taiki-e/install-action@cargo-llvm-cov

- name: Generate LCOV report
run: cargo llvm-cov --target i686-unknown-linux-gnu --lcov --output-path lcov.info

- name: Upload coverage artifact
uses: actions/upload-artifact@v7
with:
name: coverage-lcov
path: lcov.info
if-no-files-found: error
51 changes: 49 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,52 @@
# Rust build artifacts
/target
**/*.rs.bk
*.pdb

# Plugin distribution output (regenerated by scripts/build-*.sh)
/dist

# MkDocs site output (regenerated by `mkdocs build`)
/site

# Runtime logs the plugin writes (logs/env.log)
/logs

# Secrets — never commit a real .env. Use examples/env as the template.
.env
!.env.example
.claude
.env.*

# Code coverage output (cargo-llvm-cov, grcov)
lcov.info
*.profraw
*.profdata
/coverage

# Python virtualenvs used to preview docs locally (`mkdocs serve`)
.venv/
venv/
env/
__pycache__/
*.pyc

# Editor / IDE
.vscode/
.idea/
*.swp
*.swo
*~

# OS junk
.DS_Store
Thumbs.db
desktop.ini

# AI assistant scratch
.claude
.cursor/
.aider*

# Local override files
*.local
*.bak
*.orig
Loading
Loading