specter-mem 1.0.7

ARM64 memory manipulation framework for iOS/macOS — inline hooking, stealth code patching, hardware breakpoints, and shellcode loading
name: CI

on:
  push:
    branches: [main]
    tags: ['v[0-9]+.[0-9]+.[0-9]+*']
  pull_request:
    branches: [main]

env:
  CARGO_TERM_COLOR: always
  RUST_BACKTRACE: 1

jobs:
  lint:
    name: Lint
    runs-on: macos-latest
    steps:
      - uses: actions/checkout@v4

      - name: Install Rust toolchain
        run: |
          rustup toolchain install stable \
            --profile minimal \
            --target aarch64-apple-ios \
            --target aarch64-apple-darwin \
            --component rustfmt \
            --component clippy
          rustup default stable

      - name: Cache cargo registry
        uses: actions/cache@v4
        with:
          path: |
            ~/.cargo/registry/index
            ~/.cargo/registry/cache
            ~/.cargo/git/db
            target
          key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
          restore-keys: |
            ${{ runner.os }}-cargo-

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

      - name: Clippy (host target — logic only)
        run: cargo clippy --lib -- -D warnings

  build:
    name: Build & Verify
    runs-on: macos-latest
    steps:
      - uses: actions/checkout@v4

      - name: Install Rust toolchain
        run: |
          rustup toolchain install stable \
            --profile minimal \
            --target aarch64-apple-ios \
            --target aarch64-apple-darwin
          rustup default stable

      - name: Cache cargo registry
        uses: actions/cache@v4
        with:
          path: |
            ~/.cargo/registry/index
            ~/.cargo/registry/cache
            ~/.cargo/git/db
            target
          key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
          restore-keys: |
            ${{ runner.os }}-cargo-

      - name: Type-check (iOS)
        run: cargo check --target aarch64-apple-ios

      - name: Type-check (macOS)
        run: cargo check --target aarch64-apple-darwin

      - name: Build libspecter.a (iOS + macOS)
        run: make

      - name: Verify exported symbols (iOS + macOS)
        run: make check

      - name: Upload iOS artifact
        uses: actions/upload-artifact@v4
        with:
          name: libspecter-ios-${{ github.sha }}
          path: |
            target/aarch64-apple-ios/release/libspecter.a
            specter.h
          retention-days: 7

      - name: Upload macOS artifact
        uses: actions/upload-artifact@v4
        with:
          name: libspecter-macos-${{ github.sha }}
          path: |
            target/aarch64-apple-darwin/release/libspecter.a
            specter.h
          retention-days: 7

  publish-package:
    name: Publish GitHub Package
    needs: [lint, build]
    if: startsWith(github.ref, 'refs/tags/v')
    runs-on: macos-latest
    permissions:
      contents: read
      packages: write
    steps:
      - uses: actions/checkout@v4

      - name: Install Rust toolchain
        run: |
          rustup toolchain install stable \
            --profile minimal \
            --target aarch64-apple-ios \
            --target aarch64-apple-darwin
          rustup default stable

      - name: Cache cargo registry
        uses: actions/cache@v4
        with:
          path: |
            ~/.cargo/registry/index
            ~/.cargo/registry/cache
            ~/.cargo/git/db
            target
          key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
          restore-keys: |
            ${{ runner.os }}-cargo-

      - name: Build libspecter.a (iOS + macOS)
        run: make

      - name: Install ORAS
        uses: oras-project/setup-oras@v1

      - name: Push artifacts to GitHub Container Registry
        run: |
          echo "${{ github.token }}" | oras login ghcr.io \
            --username "${{ github.actor }}" --password-stdin

          REPO_LC=$(echo "$GITHUB_REPOSITORY" | tr '[:upper:]' '[:lower:]')

          oras push "ghcr.io/${REPO_LC}:${{ github.ref_name }}" \
            --artifact-type "application/vnd.specter.library.v1" \
            target/aarch64-apple-ios/release/libspecter.a:application/octet-stream \
            target/aarch64-apple-darwin/release/libspecter.a:application/octet-stream \
            specter.h:text/plain

          if [[ "${{ github.ref_name }}" != *-* ]]; then
            oras tag "ghcr.io/${REPO_LC}:${{ github.ref_name }}" latest
          fi

  release:
    name: Publish Release
    needs: [lint, build]
    if: startsWith(github.ref, 'refs/tags/v')
    runs-on: macos-latest
    permissions:
      contents: write
    steps:
      - uses: actions/checkout@v4

      - name: Install Rust toolchain
        run: |
          rustup toolchain install stable \
            --profile minimal \
            --target aarch64-apple-ios \
            --target aarch64-apple-darwin
          rustup default stable

      - name: Cache cargo registry
        uses: actions/cache@v4
        with:
          path: |
            ~/.cargo/registry/index
            ~/.cargo/registry/cache
            ~/.cargo/git/db
            target
          key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
          restore-keys: |
            ${{ runner.os }}-cargo-

      - name: Build libspecter.a (iOS + macOS)
        run: make

      - name: Verify exported symbols
        run: make check

      - name: Package release artifacts
        run: |
          IOS_ARCHIVE="specter-${{ github.ref_name }}-aarch64-apple-ios.tar.gz"
          tar -czf "$IOS_ARCHIVE" \
            -C target/aarch64-apple-ios/release libspecter.a \
            -C "$GITHUB_WORKSPACE" specter.h
          shasum -a 256 "$IOS_ARCHIVE" > "$IOS_ARCHIVE.sha256"

          MACOS_ARCHIVE="specter-${{ github.ref_name }}-aarch64-apple-darwin.tar.gz"
          tar -czf "$MACOS_ARCHIVE" \
            -C target/aarch64-apple-darwin/release libspecter.a \
            -C "$GITHUB_WORKSPACE" specter.h
          shasum -a 256 "$MACOS_ARCHIVE" > "$MACOS_ARCHIVE.sha256"

          echo "IOS_ARCHIVE=$IOS_ARCHIVE" >> "$GITHUB_ENV"
          echo "MACOS_ARCHIVE=$MACOS_ARCHIVE" >> "$GITHUB_ENV"

      - name: Create GitHub Release
        env:
          GH_TOKEN: ${{ github.token }}
        run: |
          PRERELEASE=""
          if [[ "${{ github.ref_name }}" == *-* ]]; then
            PRERELEASE="--prerelease"
          fi
          gh release create "${{ github.ref_name }}" \
            --title "Specter ${{ github.ref_name }}" \
            --generate-notes \
            $PRERELEASE \
            "${{ env.IOS_ARCHIVE }}" \
            "${{ env.IOS_ARCHIVE }}.sha256" \
            "${{ env.MACOS_ARCHIVE }}" \
            "${{ env.MACOS_ARCHIVE }}.sha256"