build: unify project development commands

This commit is contained in:
2026-07-21 18:07:06 +02:00
parent 9e8564e013
commit a8a873c83d
22 changed files with 441 additions and 246 deletions

View File

@@ -8,6 +8,9 @@ on:
- "crates/uniffi-bindgen/**"
- "Cargo.toml"
- "Cargo.lock"
- "Makefile"
- "config.mk"
- "make/**"
- ".github/workflows/apple.yml"
push:
branches:
@@ -18,6 +21,9 @@ on:
- "crates/uniffi-bindgen/**"
- "Cargo.toml"
- "Cargo.lock"
- "Makefile"
- "config.mk"
- "make/**"
- ".github/workflows/apple.yml"
permissions:
@@ -57,23 +63,5 @@ jobs:
- name: Install XcodeGen
run: brew install xcodegen
- name: Build Rust core (xcframework + Swift bindings)
working-directory: apple
run: ./scripts/build-core.sh debug
- name: Generate Xcode project
working-directory: apple
run: xcodegen generate
- name: Run unit tests (iOS Simulator)
working-directory: apple
run: |
set -euo pipefail
DEVICE=$(xcrun simctl list devices available \
| grep -oE 'iPhone [0-9]+( Pro)?' | head -1)
echo "Testing on: ${DEVICE:-iPhone 16}"
xcodebuild test \
-project VniDrop.xcodeproj \
-scheme VniDrop \
-destination "platform=iOS Simulator,name=${DEVICE:-iPhone 16}" \
CODE_SIGNING_ALLOWED=NO
- name: Build and test Apple app
run: make check-apple

View File

@@ -4,12 +4,18 @@ on:
pull_request:
paths:
- "services/diagnostics-api/**"
- "Makefile"
- "config.mk"
- "make/**"
- ".github/workflows/diagnostics-api.yml"
push:
branches:
- master
paths:
- "services/diagnostics-api/**"
- "Makefile"
- "config.mk"
- "make/**"
- ".github/workflows/diagnostics-api.yml"
permissions:
@@ -23,9 +29,6 @@ jobs:
quality:
runs-on: ubuntu-latest
timeout-minutes: 15
defaults:
run:
working-directory: services/diagnostics-api
steps:
- name: Checkout
uses: actions/checkout@v4
@@ -37,17 +40,5 @@ jobs:
cache: npm
cache-dependency-path: services/diagnostics-api/package-lock.json
- name: Install dependencies
run: npm ci
- name: Verify generated Worker types
run: npm run types:check
- name: Type-check
run: npm run typecheck
- name: Test in the Workers runtime
run: npm test
- name: Validate the deployment bundle
run: npm run deploy:dry-run
- name: Check diagnostics API
run: make check-diagnostics

View File

@@ -4,12 +4,18 @@ on:
pull_request:
paths:
- "docs/**"
- "Makefile"
- "config.mk"
- "make/**"
- ".github/workflows/docs.yml"
push:
branches:
- master
paths:
- "docs/**"
- "Makefile"
- "config.mk"
- "make/**"
- ".github/workflows/docs.yml"
permissions:
@@ -23,9 +29,6 @@ jobs:
quality:
runs-on: ubuntu-latest
timeout-minutes: 15
defaults:
run:
working-directory: docs
steps:
- name: Checkout
uses: actions/checkout@v4
@@ -37,11 +40,5 @@ jobs:
cache: npm
cache-dependency-path: docs/package-lock.json
- name: Install dependencies
run: npm ci
- name: Type-check
run: npm run typecheck
- name: Build
run: npm run build
- name: Check documentation website
run: make check-docs

View File

@@ -17,6 +17,9 @@ on:
- "gradle.properties"
- "gradle/**"
- "gradlew"
- "Makefile"
- "config.mk"
- "make/**"
push:
tags:
- "v*.*.*"
@@ -92,37 +95,7 @@ jobs:
echo "app=$version" >> "$GITHUB_OUTPUT"
- name: Test and build Debian package
run: |
./gradlew \
:shared:jvmTest \
:desktopApp:packageReleaseDeb \
-Pvnidrop.version=${{ steps.version.outputs.app }} \
-Pvnidrop.desktop.rustVariant=release \
-Pvnidrop.diagnostics.included=false \
--no-daemon \
--no-configuration-cache \
--stacktrace
- name: Validate Debian package
id: package
env:
VERSION: ${{ steps.version.outputs.app }}
run: |
mapfile -t packages < <(find desktopApp/build/compose/binaries/main-release/deb -maxdepth 1 -type f -name '*.deb')
if (( ${#packages[@]} != 1 )); then
echo "Expected exactly one Debian package, found ${#packages[@]}" >&2
exit 1
fi
output_directory=build/release/linux/deb
output_name="vnidrop_${VERSION}-1_amd64.deb"
mkdir -p "$output_directory"
cp "${packages[0]}" "$output_directory/$output_name"
packaging/linux/verify-package.sh deb "$VERSION" "$output_directory/$output_name"
(
cd "$output_directory"
sha256sum "$output_name" > "$output_name.sha256"
)
run: make package-deb VERSION=${{ steps.version.outputs.app }}
- name: Upload Debian artifact
if: github.event_name != 'pull_request'
@@ -225,35 +198,7 @@ jobs:
echo "app=$version" >> "$GITHUB_OUTPUT"
- name: Build RPM package
run: |
./gradlew \
:desktopApp:packageReleaseRpm \
-Pvnidrop.version=${{ steps.version.outputs.app }} \
-Pvnidrop.desktop.rustVariant=release \
-Pvnidrop.diagnostics.included=false \
--no-daemon \
--no-configuration-cache \
--stacktrace
- name: Validate RPM package
env:
VERSION: ${{ steps.version.outputs.app }}
run: |
mapfile -t packages < <(find desktopApp/build/compose/binaries/main-release/rpm -maxdepth 1 -type f -name '*.rpm')
if (( ${#packages[@]} != 1 )); then
echo "Expected exactly one RPM package, found ${#packages[@]}" >&2
exit 1
fi
output_directory=build/release/linux/rpm
output_name="vnidrop-${VERSION}-1.x86_64.rpm"
mkdir -p "$output_directory"
cp "${packages[0]}" "$output_directory/$output_name"
packaging/linux/verify-package.sh rpm "$VERSION" "$output_directory/$output_name"
(
cd "$output_directory"
sha256sum "$output_name" > "$output_name.sha256"
)
run: make package-rpm VERSION=${{ steps.version.outputs.app }}
- name: Upload RPM artifact
if: github.event_name != 'pull_request'

View File

@@ -6,6 +6,9 @@ on:
- "Cargo.toml"
- "Cargo.lock"
- "crates/vnidrop/**"
- "Makefile"
- "config.mk"
- "make/**"
- ".github/workflows/rust-core.yml"
push:
# Only after merge (or direct master pushes). Feature-branch work is covered
@@ -16,6 +19,9 @@ on:
- "Cargo.toml"
- "Cargo.lock"
- "crates/vnidrop/**"
- "Makefile"
- "config.mk"
- "make/**"
- ".github/workflows/rust-core.yml"
permissions:
@@ -33,18 +39,10 @@ jobs:
- uses: actions/checkout@v4
- name: Install Rust quality components
run: rustup component add clippy rustfmt
- name: Check formatting
run: cargo fmt --all -- --check
- name: Run strict Clippy
run: cargo clippy --workspace --all-targets -- -D warnings
- name: Run unit and integration tests
run: cargo test --workspace --all-targets
- name: Check documentation
env:
RUSTDOCFLAGS: -D warnings
run: cargo doc --workspace --no-deps
- name: Check Rust core
run: make check-rust
- name: Install cargo-audit
run: cargo install cargo-audit --locked
- name: Audit Rust dependencies
# Ignores are listed in .cargo/audit.toml for known transitive issues.
run: cargo audit
run: make audit-rust

View File

@@ -15,6 +15,9 @@ on:
- "gradle.properties"
- "androidApp/**"
- "desktopApp/**"
- "Makefile"
- "config.mk"
- "make/**"
- ".github/workflows/shared-kmp.yml"
push:
branches:
@@ -32,6 +35,9 @@ on:
- "gradle.properties"
- "androidApp/**"
- "desktopApp/**"
- "Makefile"
- "config.mk"
- "make/**"
- ".github/workflows/shared-kmp.yml"
permissions:
@@ -94,7 +100,7 @@ jobs:
echo "ANDROID_NDK_ROOT=${{ steps.setup-ndk.outputs.ndk-path }}" >> "$GITHUB_ENV"
- name: Run shared JVM tests
run: ./gradlew :shared:jvmTest --no-daemon --stacktrace
run: make check-shared
- name: Verify Android native libraries
run: ./gradlew :androidApp:verifyDebugVnidropLibraries --no-daemon --stacktrace
run: make verify-android-libs

1
.gitignore vendored
View File

@@ -19,6 +19,7 @@ captures
node_modules/
target/
.junie
config.override.mk
# Local design export scratch
output/

View File

@@ -53,7 +53,7 @@ Domain docs (reference, do not paste into PRs):
## Build and test
Install prerequisites when missing: Rust stable + rustfmt + clippy, JDK 17,
Install prerequisites when missing: GNU Make + Bash, Rust stable + rustfmt + clippy, JDK 17,
Android NDK/SDK only if building Android, Xcode only for the native Apple app.
### Rust core (`crates/vnidrop` or workspace root)
@@ -61,43 +61,40 @@ Android NDK/SDK only if building Android, Xcode only for the native Apple app.
Run from the **repo root** (Cargo workspace):
```bash
cargo fmt --all -- --check
cargo clippy --workspace --all-targets -- -D warnings
cargo test --workspace --all-targets
make check-rust
```
Focused:
```bash
cargo test -p vnidrop
cargo test -p vnidrop --test output_sink
cargo test -p vnidrop --test transfer
cargo test -p vnidrop --test approval
cargo test -p vnidrop --test lifecycle
make test-rust
make test-rust-output-sink
make test-rust-transfer
make test-rust-approval
make test-rust-lifecycle
```
After finishing Rust edits, format:
```bash
cargo fmt --all
make format
```
CI also runs `cargo doc --workspace --no-deps` with `RUSTDOCFLAGS=-D warnings`
(see `.github/workflows/rust-core.yml`). Run it before large Rust public-API changes.
`make check-rust` includes documentation with warnings denied, matching
`.github/workflows/rust-core.yml`.
### Shared KMP / Compose (`shared/`)
```bash
./gradlew :shared:jvmTest
./gradlew :shared:compileKotlinJvm
make check-shared
```
Other targets (slower / machine-dependent):
```bash
./gradlew :shared:testAndroidHostTest
./gradlew :androidApp:assembleDebug
./gradlew :desktopApp:run
make test-android-host
make check-android
make run-desktop
```
**Note:** `jvmTest` CI runs on **Linux**. Gobley host cargo is enabled for the
@@ -108,10 +105,10 @@ Rust library.
| You changed… | Minimum verification |
|--------------|----------------------|
| `crates/vnidrop/**` only | `cargo fmt`, `cargo clippy … -D warnings`, `cargo test -p vnidrop` |
| Cancel / export / sinks | Above + `cargo test -p vnidrop --test output_sink` |
| `shared/**` only | `./gradlew :shared:jvmTest` |
| Both | Rust suite + `:shared:jvmTest` |
| `crates/vnidrop/**` only | `make check-rust` |
| Cancel / export / sinks | Above + `make test-rust-output-sink` |
| `shared/**` only | `make test-shared` |
| Both | `make test-rust test-shared` |
| Docs only | No suite required; verify links/paths |
Do not kill long `cargo` / Gradle runs mid-flight unless they hang past several

View File

@@ -35,6 +35,7 @@ Use a branch name that describes the outcome, such as
Install the tools needed for the area you plan to change:
- GNU Make and Bash for the root command interface
- JDK 17 or newer for Gradle and application builds
- Rust stable with `rustfmt` and Clippy for the transfer core
- Android SDK and NDK for Android builds
@@ -44,6 +45,25 @@ Install the tools needed for the area you plan to change:
The first Rust and Gradle builds may take several minutes while dependencies are
downloaded and native components are compiled.
## Command Interface
Run development commands through the root `Makefile`. It keeps local and CI
commands aligned while continuing to delegate builds to Cargo, Gradle, Xcode,
Bun, and npm:
```bash
make help # list commands
make doctor # report missing host tools
make setup # install repository-local JavaScript dependencies
make check # portable Rust, shared, localization, docs, and service checks
```
Configuration can be passed on the command line, for example
`make package-deb VERSION=1.2.0`, or placed in an ignored
`config.override.mk`. Windows use requires GNU Make in a Bash environment; the
underlying Gradle and PowerShell entry points remain available when Make is not
installed.
## Repository Structure
| Path | Purpose |
@@ -87,55 +107,48 @@ Run checks from the repository root. Choose the suite for the files you changed.
### Rust Core
```bash
cargo fmt --all
cargo clippy --workspace --all-targets -- -D warnings
cargo test -p vnidrop
make format
make test-rust
```
For cancel, export, or output-sink changes, also run:
```bash
cargo test -p vnidrop --test output_sink
make test-rust-output-sink
```
For broader core changes, run the complete workspace suite:
```bash
cargo test --workspace --all-targets
make check-rust
```
### Shared Kotlin and Compose
```bash
./gradlew :shared:jvmTest
make test-shared
```
Platform-specific checks may also be appropriate:
```bash
./gradlew :shared:testAndroidHostTest
./gradlew :androidApp:assembleDebug
make test-android-host
make check-android
```
### Native Apple App
```bash
cd apple
./scripts/build-core.sh debug
xcodegen generate
xcodebuild test \
-project VniDrop.xcodeproj \
-scheme VniDrop \
-destination 'platform=iOS Simulator,name=iPhone 16' \
CODE_SIGNING_ALLOWED=NO
make check-apple
```
Override the selected simulator when needed with
`make check-apple APPLE_DESTINATION='platform=iOS Simulator,name=iPhone 16'`.
### Diagnostics Service
```bash
cd services/diagnostics-api
npm ci
npm run check
make check-diagnostics
```
If a required check cannot run on your machine, explain why in the pull request

181
Makefile Normal file
View File

@@ -0,0 +1,181 @@
ROOT := $(patsubst %/,%,$(dir $(abspath $(lastword $(MAKEFILE_LIST)))))
SHELL := bash
.SHELLFLAGS := -eu -o pipefail -c
.DEFAULT_GOAL := help
include $(ROOT)/config.mk
-include $(ROOT)/config.override.mk
include $(ROOT)/make/release.mk
.PHONY: help doctor setup setup-localization setup-docs setup-diagnostics
.PHONY: format test check check-rust audit-rust test-rust test-rust-all
.PHONY: test-rust-transfer test-rust-approval test-rust-lifecycle test-rust-output-sink
.PHONY: check-shared test-shared test-android-host check-android verify-android-libs build-android run-desktop
.PHONY: apple-core apple-project open-apple-project open-apple build-apple-macos build-apple-ios check-apple
.PHONY: check-localization localization localization-migrate
.PHONY: check-docs run-docs check-diagnostics run-diagnostics diagnostics-db-local diagnostics-db-remote diagnostics-typegen deploy-diagnostics
help: ## Show available commands and common configuration variables.
@grep -hE '^[A-Za-z0-9_.-]+:.*## ' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*## "} {printf " %-28s %s\n", $$1, $$2}'
@printf '\nCommon variables:\n'
@printf ' %-28s %s\n' 'VERSION=x.y.z' 'Package version (default: $(VERSION))'
@printf ' %-28s %s\n' 'APPLE_PROFILE=debug|release' 'Rust profile for the Apple XCFramework'
@printf ' %-28s %s\n' 'APPLE_CONFIGURATION=...' 'Xcode configuration (default: $(APPLE_CONFIGURATION))'
@printf ' %-28s %s\n' 'APPLE_DESTINATION=...' 'Optional xcodebuild destination override'
@printf ' %-28s %s\n' 'APPLE_CODE_SIGNING=NO|YES' 'Enable Apple code signing (default: $(APPLE_CODE_SIGNING))'
doctor: ## Check that tools required by the current host are available.
@missing=0; \
for tool in "$(firstword $(CARGO))" java "$(firstword $(NPM))" "$(firstword $(BUN))"; do \
if command -v "$$tool" >/dev/null 2>&1; then \
printf 'ok %s\n' "$$tool"; \
else \
printf 'missing %s\n' "$$tool"; \
missing=1; \
fi; \
done; \
if [[ ! -f "$(GRADLE)" ]]; then printf 'missing %s\n' "$(GRADLE)"; missing=1; else printf 'ok %s\n' "$(GRADLE)"; fi; \
if [[ "$(HOST_OS)" == macos ]]; then \
for tool in "$(firstword $(XCODEBUILD))" "$(firstword $(XCODEGEN))"; do \
if command -v "$$tool" >/dev/null 2>&1; then printf 'ok %s\n' "$$tool"; else printf 'missing %s\n' "$$tool"; missing=1; fi; \
done; \
fi; \
exit $$missing
setup: setup-localization setup-docs setup-diagnostics ## Install repository-local JavaScript dependencies.
setup-localization: ## Install localization CLI dependencies with Bun.
cd $(ROOT)/localization && $(BUN) install --frozen-lockfile
setup-docs: ## Install documentation website dependencies.
cd $(ROOT)/docs && $(NPM) ci
setup-diagnostics: ## Install diagnostics Worker dependencies.
cd $(ROOT)/services/diagnostics-api && $(NPM) ci
format: ## Format Rust sources.
cd $(ROOT) && $(CARGO) fmt --all
test: test-rust test-shared ## Run the main Rust and shared JVM test suites.
check: check-rust check-shared check-localization check-docs check-diagnostics ## Run portable pre-PR verification.
check-rust: ## Run Rust formatting, lint, tests, and documentation checks.
cd $(ROOT) && $(CARGO) fmt --all -- --check
cd $(ROOT) && $(CARGO) clippy --workspace --all-targets -- -D warnings
cd $(ROOT) && $(CARGO) test --workspace --all-targets
cd $(ROOT) && RUSTDOCFLAGS='-D warnings' $(CARGO) doc --workspace --no-deps
audit-rust: ## Audit Rust dependencies (requires cargo-audit).
cd $(ROOT) && $(CARGO) audit
test-rust: ## Run the focused Rust core suite.
cd $(ROOT) && $(CARGO) test -p vnidrop
test-rust-all: ## Run every Rust workspace test target.
cd $(ROOT) && $(CARGO) test --workspace --all-targets
test-rust-transfer: ## Run Rust transfer integration tests.
cd $(ROOT) && $(CARGO) test -p vnidrop --test transfer
test-rust-approval: ## Run Rust approval integration tests.
cd $(ROOT) && $(CARGO) test -p vnidrop --test approval
test-rust-lifecycle: ## Run Rust lifecycle integration tests.
cd $(ROOT) && $(CARGO) test -p vnidrop --test lifecycle
test-rust-output-sink: ## Run Rust output-sink integration tests.
cd $(ROOT) && $(CARGO) test -p vnidrop --test output_sink
check-shared: ## Test and compile the shared Android/JVM module.
cd $(ROOT) && $(GRADLE) :shared:jvmTest :shared:compileKotlinJvm $(GRADLE_FLAGS)
test-shared: ## Run shared JVM tests.
cd $(ROOT) && $(GRADLE) :shared:jvmTest $(GRADLE_FLAGS)
test-android-host: ## Run Android host-side shared tests.
cd $(ROOT) && $(GRADLE) :shared:testAndroidHostTest $(GRADLE_FLAGS)
check-android: ## Build Android debug and verify packaged Rust libraries.
cd $(ROOT) && $(GRADLE) :androidApp:assembleDebug :androidApp:verifyDebugVnidropLibraries $(GRADLE_FLAGS)
verify-android-libs: ## Verify the Rust libraries packaged in the Android debug app.
cd $(ROOT) && $(GRADLE) :androidApp:verifyDebugVnidropLibraries $(GRADLE_FLAGS)
build-android: ## Build the Android debug APK.
cd $(ROOT) && $(GRADLE) :androidApp:assembleDebug $(GRADLE_FLAGS)
run-desktop: ## Run the Windows/Linux Compose desktop app.
cd $(ROOT) && $(GRADLE) :desktopApp:run $(GRADLE_FLAGS)
apple-core: ## Build the Rust XCFramework and generated Swift bindings.
@test "$(HOST_OS)" = macos || { printf 'Apple builds require macOS.\n' >&2; exit 1; }
cd $(ROOT) && apple/scripts/build-core.sh $(APPLE_PROFILE)
apple-project: apple-core ## Generate the native Apple Xcode project.
cd $(ROOT)/apple && $(XCODEGEN) generate
open-apple-project: apple-project ## Generate and open the native Apple Xcode project.
cd $(ROOT)/apple && $(OPEN) VniDrop.xcodeproj
build-apple-macos: apple-project ## Build the native macOS app (unsigned by default).
cd $(ROOT)/apple && $(XCODEBUILD) -project VniDrop.xcodeproj -scheme VniDrop -configuration $(APPLE_CONFIGURATION) -derivedDataPath "$(APPLE_DERIVED_DATA)" -destination 'platform=macOS' CODE_SIGNING_ALLOWED=$(APPLE_CODE_SIGNING) CODE_SIGNING_REQUIRED=$(APPLE_CODE_SIGNING) build
open-apple: build-apple-macos ## Build and launch the native macOS app.
@test -d "$(APPLE_DERIVED_DATA)/Build/Products/$(APPLE_CONFIGURATION)/VniDrop.app" || { printf 'Built macOS app was not found.\n' >&2; exit 1; }
$(OPEN) "$(APPLE_DERIVED_DATA)/Build/Products/$(APPLE_CONFIGURATION)/VniDrop.app"
build-apple-ios: apple-project ## Build the native iOS simulator app (unsigned by default).
@destination="$(APPLE_DESTINATION)"; \
if [[ -z "$$destination" ]]; then \
device_id="$$(xcrun simctl list devices available | sed -nE '/iPhone/ s/.*\(([0-9A-F-]{36})\) \((Booted|Shutdown)\).*/\1/p' | head -1 || true)"; \
[[ -n "$$device_id" ]] || { printf 'No available iPhone simulator found. Set APPLE_DESTINATION explicitly.\n' >&2; exit 1; }; \
destination="platform=iOS Simulator,id=$$device_id"; \
fi; \
cd $(ROOT)/apple && $(XCODEBUILD) -project VniDrop.xcodeproj -scheme VniDrop -configuration $(APPLE_CONFIGURATION) -derivedDataPath "$(APPLE_DERIVED_DATA)" -destination "$$destination" CODE_SIGNING_ALLOWED=$(APPLE_CODE_SIGNING) CODE_SIGNING_REQUIRED=$(APPLE_CODE_SIGNING) build
check-apple: apple-project ## Build the Apple core and run iOS simulator tests.
@destination="$(APPLE_DESTINATION)"; \
if [[ -z "$$destination" ]]; then \
device_id="$$(xcrun simctl list devices available | sed -nE '/iPhone/ s/.*\(([0-9A-F-]{36})\) \((Booted|Shutdown)\).*/\1/p' | head -1 || true)"; \
[[ -n "$$device_id" ]] || { printf 'No available iPhone simulator found. Set APPLE_DESTINATION explicitly.\n' >&2; exit 1; }; \
destination="platform=iOS Simulator,id=$$device_id"; \
fi; \
printf 'Testing on: %s\n' "$$destination"; \
cd $(ROOT)/apple && $(XCODEBUILD) test -project VniDrop.xcodeproj -scheme VniDrop -configuration $(APPLE_CONFIGURATION) -derivedDataPath "$(APPLE_DERIVED_DATA)" -destination "$$destination" CODE_SIGNING_ALLOWED=$(APPLE_CODE_SIGNING) CODE_SIGNING_REQUIRED=$(APPLE_CODE_SIGNING)
check-localization: setup-localization ## Validate the localization source catalog.
cd $(ROOT)/localization && $(BUN) run validate
localization: setup-localization ## Regenerate Apple and KMP localization resources.
cd $(ROOT)/localization && $(BUN) run generate
localization-migrate: setup-localization ## Rebuild strings.json from platform resources.
cd $(ROOT)/localization && $(BUN) run migrate
check-docs: setup-docs ## Lint, type-check, and build the documentation website.
cd $(ROOT)/docs && $(NPM) run lint
cd $(ROOT)/docs && $(NPM) run typecheck
cd $(ROOT)/docs && $(NPM) run build
run-docs: setup-docs ## Run the documentation development server.
cd $(ROOT)/docs && $(NPM) run dev
check-diagnostics: setup-diagnostics ## Run diagnostics types, tests, and deployment dry-run.
cd $(ROOT)/services/diagnostics-api && $(NPM) run check
run-diagnostics: setup-diagnostics ## Run the diagnostics Worker locally.
cd $(ROOT)/services/diagnostics-api && $(NPM) run dev
diagnostics-db-local: setup-diagnostics ## Apply diagnostics database migrations locally.
cd $(ROOT)/services/diagnostics-api && $(NPM) run db:migrate:local
diagnostics-db-remote: setup-diagnostics ## Apply diagnostics database migrations to the configured remote D1 database.
cd $(ROOT)/services/diagnostics-api && $(NPM) run db:migrate:remote
diagnostics-typegen: setup-diagnostics ## Regenerate diagnostics Worker binding types.
cd $(ROOT)/services/diagnostics-api && $(NPM) run typegen
deploy-diagnostics: setup-diagnostics ## Check and deploy the diagnostics Worker to Cloudflare.
cd $(ROOT)/services/diagnostics-api && $(NPM) run deploy

View File

@@ -118,17 +118,21 @@ if you want to try the current version.
git clone https://github.com/vnidrop/vnidrop.git
cd vnidrop
# List the supported development commands and check prerequisites
make help
make doctor
# Windows/Linux desktop
./gradlew :desktopApp:run
make run-desktop
# Android debug build
./gradlew :androidApp:assembleDebug
make build-android
# iOS, iPadOS, and macOS
cd apple
./scripts/build-core.sh debug
xcodegen generate
open VniDrop.xcodeproj
# Build and launch the macOS app
make open-apple
# Open the native project for iOS, iPadOS, or Xcode development
make open-apple-project
```
See [`CONTRIBUTING.md`](CONTRIBUTING.md) for prerequisites, development setup,

View File

@@ -30,21 +30,24 @@ Prerequisites: Xcode, Rust with the Apple targets
`aarch64-apple-darwin`), and `xcodegen` (`brew install xcodegen`).
```bash
# 1. Build the Rust core and generate the Swift bindings + xcframework.
apple/scripts/build-core.sh debug # or: release (see note below)
# 2. Generate the Xcode project.
cd apple && xcodegen generate
# 3. Open and run, or build from the CLI:
open VniDrop.xcodeproj
# macOS:
xcodebuild -project VniDrop.xcodeproj -scheme VniDrop -destination 'platform=macOS' build
# iOS simulator:
xcodebuild -project VniDrop.xcodeproj -scheme VniDrop \
-destination 'platform=iOS Simulator,name=iPhone 15' build
# From the repository root:
make apple-core # Rust core, Swift bindings, and XCFramework
make apple-project # generate apple/VniDrop.xcodeproj
make open-apple-project # generate and open the project in Xcode
make build-apple-macos # unsigned macOS build
make open-apple # build and launch the macOS app
make build-apple-ios # unsigned iOS simulator build
make check-apple # iOS simulator tests
```
Use `APPLE_PROFILE=release` to request a release Rust core, or set
`APPLE_DESTINATION` to override the automatically selected iOS simulator.
Code signing is disabled for the app and test targets; local and CI builds do
not require an Apple Development team or provisioning profile. Make builds can
opt in with `APPLE_CODE_SIGNING=YES`. For signed builds from Xcode, create the
ignored `apple/Local.xcconfig` and override the signing settings there, including
the development team.
## Command-line typecheck & tests
`Package.swift` builds the same sources as a library (minus the `@main` entry),

View File

@@ -1,9 +1,6 @@
// Committed signing config. Contains no secrets.
//
// Per-developer signing (e.g. DEVELOPMENT_TEAM) goes in Local.xcconfig, which is
// gitignored. The optional include below means the build still works for anyone
// who doesn't have a Local.xcconfig — Xcode automatic signing fills in their team.
//
// To persist your team across `xcodegen generate`, create apple/Local.xcconfig:
// DEVELOPMENT_TEAM = XXXXXXXXXX
// VniDrop development and CI builds are intentionally unsigned.
CODE_SIGNING_ALLOWED = NO
CODE_SIGNING_REQUIRED = NO
// Signed local builds can opt in through this ignored file.
#include? "Local.xcconfig"

View File

@@ -41,7 +41,6 @@ targets:
SWIFT_STRICT_CONCURRENCY: complete
ENABLE_USER_SCRIPT_SANDBOXING: NO
ASSETCATALOG_COMPILER_APPICON_NAME: AppIcon
CODE_SIGN_STYLE: Automatic
configs:
debug:
CODE_SIGN_ENTITLEMENTS: VniDrop/Resources/VniDrop.entitlements
@@ -56,6 +55,9 @@ targets:
VniDropTests:
type: bundle.unit-test
supportedDestinations: [iOS, macOS]
configFiles:
Debug: Signing.xcconfig
Release: Signing.xcconfig
sources:
- path: Tests
settings:
@@ -63,7 +65,6 @@ targets:
GENERATE_INFOPLIST_FILE: YES
SWIFT_VERSION: "6.0"
SWIFT_STRICT_CONCURRENCY: complete
CODE_SIGN_STYLE: Automatic
dependencies:
- target: VniDrop

35
config.mk Normal file
View File

@@ -0,0 +1,35 @@
# Default command configuration. Override locally in the ignored
# config.override.mk or on the command line (for example: make package-deb VERSION=1.2.0).
ifeq ($(OS),Windows_NT)
HOST_OS := windows
GRADLE ?= ./gradlew.bat
else
HOST_UNAME := $(shell uname -s)
ifeq ($(HOST_UNAME),Darwin)
HOST_OS := macos
else ifeq ($(HOST_UNAME),Linux)
HOST_OS := linux
else
HOST_OS := unknown
endif
GRADLE ?= ./gradlew
endif
CARGO ?= cargo
NPM ?= npm
BUN ?= bun
XCODEBUILD ?= xcodebuild
XCODEGEN ?= xcodegen
OPEN ?= open
POWERSHELL ?= pwsh
VERSION ?= $(shell sed -n 's/^vnidrop.version=//p' $(ROOT)/gradle.properties)
APPLE_PROFILE ?= debug
APPLE_CONFIGURATION ?= Debug
APPLE_DESTINATION ?=
APPLE_CODE_SIGNING ?= NO
APPLE_DERIVED_DATA ?= $(ROOT)/apple/DerivedData
GRADLE_FLAGS ?= --no-daemon --stacktrace
GRADLE_RELEASE_FLAGS ?= --no-daemon --no-configuration-cache --stacktrace

View File

@@ -19,20 +19,18 @@ Read [`CORE_FLOW.md`](CORE_FLOW.md) before changing send/receive/export/cancel.
Always prefer workspace commands so lockfile/fmt stay consistent:
```bash
cargo fmt --all
cargo fmt --all -- --check
cargo clippy --workspace --all-targets -- -D warnings
cargo test -p vnidrop
cargo test --workspace --all-targets
make format
make test-rust
make check-rust
```
Focused integration suites:
```bash
cargo test -p vnidrop --test transfer
cargo test -p vnidrop --test approval
cargo test -p vnidrop --test lifecycle
cargo test -p vnidrop --test output_sink
make test-rust-transfer
make test-rust-approval
make test-rust-lifecycle
make test-rust-output-sink
```
Docs (CI uses `-D warnings`):
@@ -41,7 +39,7 @@ Docs (CI uses `-D warnings`):
RUSTDOCFLAGS='-D warnings' cargo doc -p vnidrop --no-deps
```
Run `cargo fmt --all` after finishing Rust edits without asking.
Run `make format` after finishing Rust edits without asking.
---
@@ -116,13 +114,11 @@ Details: [`tests/README.md`](tests/README.md).
## PR / verify checklist for this crate
```bash
cargo fmt --all -- --check
cargo clippy --workspace --all-targets -- -D warnings
cargo test -p vnidrop
make check-rust
```
If you touched cancel, export, or sinks, also:
```bash
cargo test -p vnidrop --test output_sink
make test-rust-output-sink
```

View File

@@ -5,8 +5,8 @@ The product website for VniDrop, built with Next.js and exported as a static sit
## Local development
```bash
npm install
npm run dev
# From the repository root:
make run-docs
```
Open [http://localhost:3000](http://localhost:3000).
@@ -14,9 +14,7 @@ Open [http://localhost:3000](http://localhost:3000).
## Checks
```bash
npm run lint
npm run typecheck
npm run build
make check-docs
```
The production build is written to `out/` and can be hosted by any static web server.

View File

@@ -11,10 +11,10 @@ A Bun CLI generates the platform-native files from it:
## Workflow
```bash
cd localization
bun run src/cli.ts validate # structural checks (run before committing)
bun run src/cli.ts generate # regenerate .xcstrings + strings.xml from strings.json
bun run src/cli.ts migrate # one-time: rebuild strings.json from existing platform files
# From the repository root:
make check-localization # structural checks (run before committing)
make localization # regenerate .xcstrings + strings.xml from strings.json
make localization-migrate # one-time: rebuild strings.json from platform files
```
**Never edit the generated `.xcstrings` / `strings.xml` by hand** — edit `strings.json` and

51
make/release.mk Normal file
View File

@@ -0,0 +1,51 @@
.PHONY: package-deb package-rpm package-msix
package-deb: ## Build and verify a Debian x64 package (VERSION=x.y.z).
@test "$(HOST_OS)" = linux || { printf 'Debian packaging requires Linux.\n' >&2; exit 1; }
@cd $(ROOT); \
version="$$(packaging/linux/resolve-version.sh "$(VERSION)")"; \
$(GRADLE) :shared:jvmTest :desktopApp:packageReleaseDeb \
-Pvnidrop.version="$$version" \
-Pvnidrop.desktop.rustVariant=release \
-Pvnidrop.diagnostics.included=false \
$(GRADLE_RELEASE_FLAGS); \
mapfile -t packages < <(find desktopApp/build/compose/binaries/main-release/deb -maxdepth 1 -type f -name '*.deb'); \
(( $${#packages[@]} == 1 )) || { printf 'Expected exactly one Debian package, found %s\n' "$${#packages[@]}" >&2; exit 1; }; \
output_directory=build/release/linux/deb; \
output_name="vnidrop_$${version}-1_amd64.deb"; \
mkdir -p "$$output_directory"; \
cp "$${packages[0]}" "$$output_directory/$$output_name"; \
packaging/linux/verify-package.sh deb "$$version" "$$output_directory/$$output_name"; \
( cd "$$output_directory" && sha256sum "$$output_name" > "$$output_name.sha256" ); \
printf 'Package: %s/%s\n' "$$output_directory" "$$output_name"
package-rpm: ## Build and verify an RPM x64 package (VERSION=x.y.z).
@test "$(HOST_OS)" = linux || { printf 'RPM packaging requires Linux.\n' >&2; exit 1; }
@cd $(ROOT); \
version="$$(packaging/linux/resolve-version.sh "$(VERSION)")"; \
$(GRADLE) :desktopApp:packageReleaseRpm \
-Pvnidrop.version="$$version" \
-Pvnidrop.desktop.rustVariant=release \
-Pvnidrop.diagnostics.included=false \
$(GRADLE_RELEASE_FLAGS); \
mapfile -t packages < <(find desktopApp/build/compose/binaries/main-release/rpm -maxdepth 1 -type f -name '*.rpm'); \
(( $${#packages[@]} == 1 )) || { printf 'Expected exactly one RPM package, found %s\n' "$${#packages[@]}" >&2; exit 1; }; \
output_directory=build/release/linux/rpm; \
output_name="vnidrop-$${version}-1.x86_64.rpm"; \
mkdir -p "$$output_directory"; \
cp "$${packages[0]}" "$$output_directory/$$output_name"; \
packaging/linux/verify-package.sh rpm "$$version" "$$output_directory/$$output_name"; \
( cd "$$output_directory" && sha256sum "$$output_name" > "$$output_name.sha256" ); \
printf 'Package: %s/%s\n' "$$output_directory" "$$output_name"
package-msix: ## Build and verify an unsigned Windows Store MSIX (VERSION=x.y.z).
@test "$(HOST_OS)" = windows || { printf 'MSIX packaging requires Windows.\n' >&2; exit 1; }
cd $(ROOT) && $(GRADLE) :shared:jvmTest :desktopApp:createReleaseDistributable \
-Pvnidrop.version="$(VERSION)" \
-Pvnidrop.desktop.rustVariant=release \
-Pvnidrop.diagnostics.included=false \
$(GRADLE_RELEASE_FLAGS)
cd $(ROOT) && $(POWERSHELL) -NoProfile -File packaging/windows/build-msix.ps1 \
-Version "$(VERSION)" \
-AppImage desktopApp/build/compose/binaries/main-release/app/VniDrop \
-OutputDirectory build/release/windows

View File

@@ -64,21 +64,10 @@ Ubuntu prevents `jpackage` from discovering normal RPM dependencies.
From the repository root on the matching Linux family, run one of:
```bash
./gradlew :shared:jvmTest :desktopApp:packageReleaseDeb \
-Pvnidrop.version=1.0.0 \
-Pvnidrop.desktop.rustVariant=release \
-Pvnidrop.diagnostics.included=false \
--no-daemon --no-configuration-cache --stacktrace
./gradlew :shared:jvmTest :desktopApp:packageReleaseRpm \
-Pvnidrop.version=1.0.0 \
-Pvnidrop.desktop.rustVariant=release \
-Pvnidrop.diagnostics.included=false \
--no-daemon --no-configuration-cache --stacktrace
make package-deb VERSION=1.0.0
make package-rpm VERSION=1.0.0
```
Compose writes the packages under
`desktopApp/build/compose/binaries/main-release/deb/` and
`desktopApp/build/compose/binaries/main-release/rpm/`. The workflow then
validates package identity, version, architecture, dependencies, bundled JVM,
and release Rust payload before publishing anything.
The Make targets collect the Compose output under `build/release/linux/`, then
validate package identity, version, architecture, dependencies, bundled JVM,
and release Rust payload before generating a SHA-256 checksum.

View File

@@ -85,19 +85,23 @@ npx wrangler r2 bucket create vnidrop-diagnostics
```
Replace the placeholder `database_id` in `wrangler.jsonc` with the UUID returned
by `wrangler d1 create`. Set the ingest key interactively, apply the tracked D1
migrations, and configure the R2 retention rule once:
by `wrangler d1 create`. Set the ingest key interactively and configure the R2
retention rule once:
```bash
npx wrangler secret put INGEST_KEY
npm run db:migrate:remote
npx wrangler r2 bucket lifecycle add vnidrop-diagnostics diagnostics-retention --expire-days 90
npm run check
npm run deploy
```
`npm run deploy` also runs the complete `check` script automatically before
Wrangler changes the remote Worker.
Then apply migrations and deploy from the repository root:
```bash
make diagnostics-db-remote
make deploy-diagnostics
```
`make deploy-diagnostics` runs the complete check before Wrangler changes the
remote Worker.
The lifecycle command changes the remote bucket. Before adding or changing a
rule, inspect the current state with:
@@ -117,8 +121,9 @@ INGEST_KEY=local-development-only
Then initialize the local D1 database and run the Worker:
```bash
npm run db:migrate:local
npm run dev
# From the repository root:
make diagnostics-db-local
make run-diagnostics
```
Wrangler keeps local D1 and R2 state under the ignored `.wrangler/` directory.
@@ -133,7 +138,7 @@ Never edit an applied migration; add the next numbered SQL file instead.
bindings cannot silently drift from the Worker code:
```bash
npm run typegen # regenerate after changing bindings or vars
make diagnostics-typegen # from the repository root
npm run types:check # verify the committed file is current
```

View File

@@ -46,16 +46,15 @@ compose-skill “Existing Project Policy”: adapt to this repo; do not force-mi
From repo root:
```bash
./gradlew :shared:jvmTest
./gradlew :shared:compileKotlinJvm
make check-shared
```
Optional:
```bash
./gradlew :shared:testAndroidHostTest
./gradlew :desktopApp:run
./gradlew :androidApp:assembleDebug
make test-android-host
make run-desktop
make check-android
```
CI `:shared:jvmTest` runs on **Linux**. Gobley host cargo follows the current
@@ -113,7 +112,7 @@ Never pass a directory as a single Android FD into `SourceKind.FILE_DESCRIPTOR`.
- Prefer fakes in `commonTest` support over real UniFFI in pure unit tests.
```bash
./gradlew :shared:jvmTest
make test-shared
```
---