Skip to content

feat(register): flag a pull request that changes a register email address - #25

Merged
EquityBot merged 3 commits into
mainfrom
ryan/register-checks
Nov 1, 2026
Merged

EquityBot merged 3 commits into
mainfrom
ryan/register-checks

Conversation

@EquityBot

Copy link
Copy Markdown
Owner

Summary

A register pull request can no longer repoint an entry's privacy email without a visible flag, and the register gate's boundary checks are pinned by tests.

  • New CI job register-emails runs xtask email-changes, comparing the register with the base branch's and annotating each email address an entry gains and every address of an entry whose id is new, so renaming an entry cannot hide a changed address. Domains compare case-insensitively; local parts do not. It warns and passes, as a stale verified_at does: a changed address is often legitimate, so a reviewer checks it on the entity's own pages. An entry that does not parse fails the job.
  • The monthly evidence-drift run also reports an email that appears on none of its entry's evidence pages (raw HTML and decoded text, any case). An entry with no page, a failed page or a non-HTML page goes to the workflow log instead.
  • Tests kill the 7 cargo-mutants survivors in src/core/register.rs.

Evidence

  • Before: cargo-mutants on src/core/register.rs: 163 caught, 7 missed, 4 unviable. With email_changes and email_on_pages stubbed, the 2 positive tests fail. The renamed-entry, recased-local-part and unparsable-entry tests fail on the first version of email_changes.
    After: 170 caught, 0 missed, 4 unviable; those tests pass. cargo fmt --check, clippy -D warnings, cargo test --locked --workspace (131 tests), release build and registry check (80 entries, 0 errors) pass on Windows 11. email-changes with one head entry replaced by invalid TOML exits 1 naming the file.

Merge danger

Door: two-way. Blast radius: CI

Adds one pull-request job; the drift issue may list emails missing from their evidence pages.

Unverified

The new CI job, including its base-branch checkout, has not run on GitHub before this PR. 14 entries with email channels have no evidence page, so their emails are not checked.

@EquityBot
EquityBot enabled auto-merge (squash) November 1, 2026 10:01
@sonarqubecloud

sonarqubecloud Bot commented Nov 1, 2026

Copy link
Copy Markdown

@codacy-production

Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

🟢 Metrics 30 complexity · 0 duplication

Metric Results
Complexity 30
Duplication 0

View in Codacy

AI Reviewer: first review requested successfully. AI can make mistakes. Always validate suggestions.

Run reviewer

TIP This summary will be updated as you push new changes.

@codacy-production codacy-production Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

✅ No blocking issues noticed

No merge-blocking findings were identified. The workflow’s end-to-end behavior remains unverified because it has not run in the target CI environment; the missing scenarios below should be covered before relying on the new job operationally.

The main implementation file has increased complexity and combines several responsibilities, which presents a maintainability risk but is not a blocker.

About this PR

  • The new CI job has not yet been validated in the target GitHub workflow environment. Add or run an end-to-end workflow check covering base-branch checkout, permissions, pull-request event behavior, and warning annotation rendering.

Test suggestions

  • Detect an email address added to an existing entry and include the previous addresses in the warning
  • Treat domain casing as equivalent but local-part casing as a change
  • Flag email addresses on an entry whose ID is new or renamed
  • Fail when either register input contains invalid TOML
  • Find email addresses in raw HTML markup and decoded main text, case-insensitively
  • Report an email missing from all searchable evidence pages
  • Treat missing evidence, failed evidence fetches, and non-HTML evidence as inconclusive
  • Emit correctly escaped GitHub warning annotations for changed addresses
  • Verify the complete CI job, including base-branch checkout and command execution
Prompt proposal for missing tests
Consider implementing these tests if applicable:
1. Emit correctly escaped GitHub warning annotations for changed addresses
2. Verify the complete CI job, including base-branch checkout and command execution

TIP Improve review quality by adding custom instructions
TIP How was this review? Give us feedback

Comment thread xtask/src/main.rs
Comment on lines +232 to 323
fn email_changes(
base: &[SourceFile],
head: &[SourceFile],
) -> Result<Vec<(String, String)>, String> {
// Domains are case-insensitive; a local part may not be, so only the domain is folded.
let emails = |entry: &Entry| -> Vec<String> {
let fold = |address: &str| match address.rsplit_once('@') {
Some((local, domain)) => format!("{local}@{}", domain.to_lowercase()),
None => address.to_owned(),
};
entry
.channels
.iter()
.filter(|c| c.kind == ChannelKind::Email)
.map(|c| fold(&c.address))
.collect()
};
for (domain, ids) in mail_domains {
record(
format!("email domain {domain}"),
ids,
check_mail(&lookup, domain),
);
let parsed = |files: &[SourceFile]| -> Result<Vec<(String, Entry)>, String> {
let parse = |f: &SourceFile| {
toml::from_str(&f.text)
.map(|e| (f.name.clone(), e))
.map_err(|e| format!("{} does not parse: {e}", f.name))
};
files.iter().map(parse).collect()
};
let before: BTreeMap<String, Vec<String>> = parsed(base)?
.into_iter()
.map(|(_, entry)| (entry.id.clone(), emails(&entry)))
.collect();
let mut changes = Vec::new();
for (file, entry) in parsed(head)? {
let old = before.get(&entry.id);
for address in emails(&entry)
.into_iter()
.filter(|a| old.is_none_or(|old| !old.contains(a)))
{
let message = match old {
None => format!(
"new entry `{}` lists email {address}; check it on the entity's own pages",
entry.id
),
Some(old) => {
let was = if old.is_empty() {
"none".to_owned()
} else {
old.join(", ")
};
format!(
"`{}` gains email {address} (before: {was}); check it on the entity's own pages",
entry.id
)
}
};
changes.push((file.clone(), message));
}
}
for (url, ids) in forms {
record(
format!("web form {url}"),
ids,
check_form(agent, &lookup, url),
);
Ok(changes)
}

/// Print a GitHub warning annotation for each email address `email_changes` lists. Exits 0
/// either way, like a stale `verified_at`; an unreadable register or entry exits 1.
fn emails_changed(base: &Path, head: &Path) -> ExitCode {
let load = |dir: &Path| {
ghostsoap::io::register::load(dir)
.map_err(|e| eprintln!("error: cannot read {}: {e}", dir.display()))
};
let (Ok(base_files), Ok(head_files)) = (load(base), load(head)) else {
return ExitCode::FAILURE;
};
let changes = match email_changes(&base_files.entries, &head_files.entries) {
Ok(changes) => changes,
Err(e) => {
eprintln!("error: {e}");
return ExitCode::FAILURE;
}
};
// A workflow command escapes `%`, CR and LF in its message, and `:` and `,` too in a
// property value.
let data = |s: &str| {
s.replace('%', "%25")
.replace('\r', "%0D")
.replace('\n', "%0A")
};
for (file, message) in &changes {
let path = head.join(file).to_string_lossy().replace('\\', "/");
let path = data(&path).replace(':', "%3A").replace(',', "%2C");
let message = data(message);
println!("::warning file={path},title=Register email changed::{message}");
}
lines
println!("{} email address(es) to check", changes.len());
ExitCode::SUCCESS
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 MEDIUM RISK

Suggestion: This file now combines evidence processing, contact validation, and pull-request email comparison. Consider moving the email comparison and GitHub annotation logic into a focused module while preserving the CLI behavior and existing tests.

See Issue in Codacy

TIP React 👍 if this review is right, or reply with what's wrong and how it could be better to help improve the reviewer.

@EquityBot
EquityBot merged commit ea465d8 into main Nov 1, 2026
15 checks passed
@EquityBot
EquityBot deleted the ryan/register-checks branch November 1, 2026 10:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant