Compare commits
5 Commits
v0.9.1
...
fix/window
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2f80d1ab7f | ||
|
|
766935ec97 | ||
|
|
efe3669540 | ||
|
|
ad34fe2d9e | ||
|
|
80fcfd10ae |
11
.github/workflows/ci.yml
vendored
11
.github/workflows/ci.yml
vendored
@@ -27,6 +27,17 @@ jobs:
|
||||
- name: audit
|
||||
run: cargo install cargo-audit && cargo audit
|
||||
|
||||
check-macos:
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: clippy
|
||||
run: cargo clippy -- -D warnings
|
||||
- name: test
|
||||
run: cargo test
|
||||
|
||||
check-windows:
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
|
||||
90
.github/workflows/release.yml
vendored
90
.github/workflows/release.yml
vendored
@@ -108,3 +108,93 @@ jobs:
|
||||
*.tar.gz
|
||||
*.zip
|
||||
*.sha256
|
||||
|
||||
update-homebrew:
|
||||
needs: release
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Get version from tag
|
||||
id: version
|
||||
run: echo "version=${GITHUB_REF_NAME#v}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Download SHA256 files
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
merge-multiple: true
|
||||
|
||||
- name: Extract checksums
|
||||
id: sha
|
||||
run: |
|
||||
echo "macos_arm=$(awk '{print $1}' numa-macos-aarch64.tar.gz.sha256)" >> "$GITHUB_OUTPUT"
|
||||
echo "macos_x86=$(awk '{print $1}' numa-macos-x86_64.tar.gz.sha256)" >> "$GITHUB_OUTPUT"
|
||||
echo "linux_arm=$(awk '{print $1}' numa-linux-aarch64.tar.gz.sha256)" >> "$GITHUB_OUTPUT"
|
||||
echo "linux_x86=$(awk '{print $1}' numa-linux-x86_64.tar.gz.sha256)" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Update Homebrew formula
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
github-token: ${{ secrets.HOMEBREW_TAP_TOKEN }}
|
||||
script: |
|
||||
const version = '${{ steps.version.outputs.version }}';
|
||||
const base = `https://github.com/razvandimescu/numa/releases/download/v${version}`;
|
||||
const formula = `class Numa < Formula
|
||||
desc "Portable DNS resolver with ad blocking, .numa local service proxy, and developer overrides"
|
||||
homepage "https://github.com/razvandimescu/numa"
|
||||
license "MIT"
|
||||
version "${version}"
|
||||
|
||||
on_macos do
|
||||
if Hardware::CPU.arm?
|
||||
url "${base}/numa-macos-aarch64.tar.gz"
|
||||
sha256 "${{ steps.sha.outputs.macos_arm }}"
|
||||
else
|
||||
url "${base}/numa-macos-x86_64.tar.gz"
|
||||
sha256 "${{ steps.sha.outputs.macos_x86 }}"
|
||||
end
|
||||
end
|
||||
|
||||
on_linux do
|
||||
if Hardware::CPU.arm?
|
||||
url "${base}/numa-linux-aarch64.tar.gz"
|
||||
sha256 "${{ steps.sha.outputs.linux_arm }}"
|
||||
else
|
||||
url "${base}/numa-linux-x86_64.tar.gz"
|
||||
sha256 "${{ steps.sha.outputs.linux_x86 }}"
|
||||
end
|
||||
end
|
||||
|
||||
def install
|
||||
bin.install "numa"
|
||||
end
|
||||
|
||||
def caveats
|
||||
<<~EOS
|
||||
Numa requires root to bind port 53:
|
||||
sudo numa # start the DNS server
|
||||
sudo numa install # set as system DNS
|
||||
sudo numa service start # run as persistent service
|
||||
|
||||
Dashboard: http://localhost:5380
|
||||
EOS
|
||||
end
|
||||
|
||||
test do
|
||||
assert_match "numa", shell_output("#{bin}/numa --version")
|
||||
end
|
||||
end
|
||||
`.replace(/^ /gm, '');
|
||||
|
||||
const { data: existing } = await github.rest.repos.getContent({
|
||||
owner: 'razvandimescu',
|
||||
repo: 'homebrew-tap',
|
||||
path: 'numa.rb',
|
||||
});
|
||||
|
||||
await github.rest.repos.createOrUpdateFileContents({
|
||||
owner: 'razvandimescu',
|
||||
repo: 'homebrew-tap',
|
||||
path: 'numa.rb',
|
||||
message: `numa ${version}`,
|
||||
content: Buffer.from(formula).toString('base64'),
|
||||
sha: existing.sha,
|
||||
});
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<string>com.numa.dns</string>
|
||||
<key>ProgramArguments</key>
|
||||
<array>
|
||||
<string>/usr/local/bin/numa</string>
|
||||
<string>{{exe_path}}</string>
|
||||
</array>
|
||||
<key>RunAtLoad</key>
|
||||
<true/>
|
||||
|
||||
@@ -5,7 +5,7 @@ Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStart=/usr/local/bin/numa
|
||||
ExecStart={{exe_path}}
|
||||
Restart=always
|
||||
RestartSec=2
|
||||
StandardOutput=journal
|
||||
|
||||
@@ -903,9 +903,13 @@ pub fn uninstall_service() -> Result<(), String> {
|
||||
|
||||
/// Restart the service (kill process, launchd/systemd auto-restarts with new binary).
|
||||
pub fn restart_service() -> Result<(), String> {
|
||||
#[cfg(any(target_os = "macos", target_os = "linux"))]
|
||||
let exe_path =
|
||||
std::env::current_exe().map_err(|e| format!("failed to get current exe: {}", e))?;
|
||||
|
||||
#[cfg(any(target_os = "macos", target_os = "linux"))]
|
||||
let version = {
|
||||
match std::process::Command::new("/usr/local/bin/numa")
|
||||
match std::process::Command::new(&exe_path)
|
||||
.arg("--version")
|
||||
.output()
|
||||
{
|
||||
@@ -916,6 +920,7 @@ pub fn restart_service() -> Result<(), String> {
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
let exe_path = exe_path.to_string_lossy();
|
||||
let output = std::process::Command::new("launchctl")
|
||||
.args(["list", PLIST_LABEL])
|
||||
.output();
|
||||
@@ -926,11 +931,11 @@ pub fn restart_service() -> Result<(), String> {
|
||||
// This will kill us too (we ARE /usr/local/bin/numa), so
|
||||
// codesign and print output first.
|
||||
let _ = std::process::Command::new("codesign")
|
||||
.args(["-f", "-s", "-", "/usr/local/bin/numa"])
|
||||
.args(["-f", "-s", "-", &exe_path])
|
||||
.output(); // use output() to suppress codesign stderr
|
||||
eprintln!(" Service restarting → {}\n", version);
|
||||
let _ = std::process::Command::new("pkill")
|
||||
.args(["-f", "/usr/local/bin/numa"])
|
||||
.args(["-f", &exe_path])
|
||||
.status();
|
||||
Ok(())
|
||||
}
|
||||
@@ -965,19 +970,23 @@ pub fn service_status() -> Result<(), String> {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
fn install_service_macos() -> Result<(), String> {
|
||||
// Check binary exists
|
||||
if !std::path::Path::new("/usr/local/bin/numa").exists() {
|
||||
return Err("numa binary not found at /usr/local/bin/numa. Run: sudo cp target/release/numa /usr/local/bin/numa".to_string());
|
||||
#[cfg(any(target_os = "macos", target_os = "linux"))]
|
||||
fn replace_exe_path(service: &str) -> Result<String, String> {
|
||||
let exe_path =
|
||||
std::env::current_exe().map_err(|e| format!("failed to get current exe: {}", e))?;
|
||||
Ok(service.replace("{{exe_path}}", &exe_path.to_string_lossy()))
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
fn install_service_macos() -> Result<(), String> {
|
||||
// Create log directory
|
||||
std::fs::create_dir_all("/usr/local/var/log")
|
||||
.map_err(|e| format!("failed to create log dir: {}", e))?;
|
||||
|
||||
// Write plist
|
||||
let plist = include_str!("../com.numa.dns.plist");
|
||||
let plist = replace_exe_path(plist)?;
|
||||
|
||||
std::fs::write(PLIST_DEST, plist)
|
||||
.map_err(|e| format!("failed to write {}: {}", PLIST_DEST, e))?;
|
||||
|
||||
@@ -1179,19 +1188,10 @@ fn uninstall_linux() -> Result<(), String> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
fn ensure_binary_installed() -> Result<(), String> {
|
||||
if !std::path::Path::new("/usr/local/bin/numa").exists() {
|
||||
return Err("numa binary not found at /usr/local/bin/numa. Run: sudo cp target/release/numa /usr/local/bin/numa".to_string());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
fn install_service_linux() -> Result<(), String> {
|
||||
ensure_binary_installed()?;
|
||||
|
||||
let unit = include_str!("../numa.service");
|
||||
let unit = replace_exe_path(unit)?;
|
||||
std::fs::write(SYSTEMD_UNIT, unit)
|
||||
.map_err(|e| format!("failed to write {}: {}", SYSTEMD_UNIT, e))?;
|
||||
|
||||
@@ -1413,6 +1413,25 @@ Wireless LAN adapter Wi-Fi:
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(target_os = "macos", target_os = "linux"))]
|
||||
fn replace_exe_path_substitutes_template() {
|
||||
let plist = include_str!("../com.numa.dns.plist");
|
||||
let unit = include_str!("../numa.service");
|
||||
|
||||
assert!(plist.contains("{{exe_path}}"), "plist missing placeholder");
|
||||
assert!(
|
||||
unit.contains("{{exe_path}}"),
|
||||
"unit file missing placeholder"
|
||||
);
|
||||
|
||||
let result = replace_exe_path(plist).expect("replace_exe_path failed for plist");
|
||||
assert!(!result.contains("{{exe_path}}"));
|
||||
|
||||
let result = replace_exe_path(unit).expect("replace_exe_path failed for unit");
|
||||
assert!(!result.contains("{{exe_path}}"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_ipconfig_skips_disconnected() {
|
||||
let sample = "\
|
||||
|
||||
Reference in New Issue
Block a user