From 6d41a5059c1027fce3146f4f68a2fbd8904fcb5f Mon Sep 17 00:00:00 2001 From: Bob Date: Fri, 18 Sep 2026 12:02:50 +0000 Subject: [PATCH 1/2] fix(ci): select nightly first-party submodules by URL, not by name Filter by whether the URL is under github.com/ActivityWatch/ (case- insensitive) instead of hard-coding a name exclusion list. This ensures newly added third-party submodules are excluded automatically, and prints the excluded set in every job run so the boundary stays visible. Closes #1456 Follow-up from #1448 Git-Session-Id: dc09 --- .github/workflows/release.yml | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0879a952c..d152d883a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -312,14 +312,28 @@ jobs: # shellcheck disable=SC2016 tips='echo "$(git rev-parse HEAD) $displaypath"' before=$(git submodule foreach --recursive --quiet "$tips") - # Move each first-party submodule (and its nested submodules) to the - # tip of its upstream default branch. awatcher is third-party and is - # left at its pinned revision: a nightly must not execute unreviewed - # third-party code in jobs that carry signing secrets. + # Build the first-party submodule list by URL, not by name: a + # submodule is first-party iff its URL is under + # github.com/ActivityWatch/ (case-insensitive). Third-party + # submodules are excluded from --remote so unreviewed code is + # never executed in jobs that carry signing secrets. + firstparty="" + excluded="" # shellcheck disable=SC2016 - firstparty=$(git submodule --quiet foreach 'echo $sm_path' | grep -vx awatcher || true) + while IFS= read -r sm; do + [ -z "$sm" ] && continue + url=$(git config -f .gitmodules --get "submodule.$sm.url" 2>/dev/null || true) + case "${url,,}" in + *github.com/activitywatch/*) firstparty="${firstparty:+$firstparty }$sm" ;; + *) excluded="${excluded:+$excluded }$sm" ;; + esac + done < <(git submodule --quiet foreach 'echo $sm_path') + # Print the excluded set every run so the boundary stays visible. + if [ -n "$excluded" ]; then + echo "Third-party submodules (excluded from --remote, kept at pinned revision): $excluded" + fi # An empty list would drop the `--` argument and move *every* - # submodule, awatcher included; refuse instead. + # submodule, including third-party; refuse instead. if [ -z "$firstparty" ]; then echo "no first-party submodules to move" >&2; exit 1; fi # shellcheck disable=SC2086 git submodule update --init --recursive --remote --depth 1 -- $firstparty 2>&1 | tail -20 From c4d0c4b5a73ce9a5897b9cc570ae9a768710f8cb Mon Sep 17 00:00:00 2001 From: Bob Date: Fri, 18 Sep 2026 12:25:48 +0000 Subject: [PATCH 2/2] fix(ci): anchor first-party URL match and derive submodule name from .gitmodules Two review findings on the URL-based classifier: - The `*github.com/activitywatch/*` glob matched the string anywhere in the URL, so a mirror like `https://mirror.example/github.com/ActivityWatch/x` would classify as first-party and get built with the signing key. Anchor the match against known URL prefixes instead. - `$sm` was populated from `git submodule foreach`'s `$sm_path`, but `.gitmodules` is keyed by submodule *name*, which git permits to differ from path. Read name/path pairs directly from `.gitmodules` instead of reusing the path as the name for the url lookup. Git-Session-Id: 1083368a-3a3e-5b8c-ac1e-c999007d3dc1 --- .github/workflows/release.yml | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d152d883a..f9b5b7ae0 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -319,15 +319,24 @@ jobs: # never executed in jobs that carry signing secrets. firstparty="" excluded="" - # shellcheck disable=SC2016 - while IFS= read -r sm; do - [ -z "$sm" ] && continue - url=$(git config -f .gitmodules --get "submodule.$sm.url" 2>/dev/null || true) + # Read name/path pairs straight from .gitmodules: the section name + # (needed for the "submodule..url" lookup) is not guaranteed + # to equal the path, so deriving the name from $sm_path would risk + # a silent empty-URL lookup for any submodule where they differ. + while read -r key path; do + [ -z "$path" ] && continue + name="${key#submodule.}" + name="${name%.path}" + url=$(git config -f .gitmodules --get "submodule.$name.url" 2>/dev/null || true) + # Anchored (no leading *) so only an actual github.com/ActivityWatch/ + # host+org prefix matches — a URL merely containing that string + # later in the path (e.g. a mirror) does not. case "${url,,}" in - *github.com/activitywatch/*) firstparty="${firstparty:+$firstparty }$sm" ;; - *) excluded="${excluded:+$excluded }$sm" ;; + https://github.com/activitywatch/*|http://github.com/activitywatch/*|git@github.com:activitywatch/*|ssh://git@github.com/activitywatch/*) + firstparty="${firstparty:+$firstparty }$path" ;; + *) excluded="${excluded:+$excluded }$path" ;; esac - done < <(git submodule --quiet foreach 'echo $sm_path') + done < <(git config -f .gitmodules --get-regexp '^submodule\..*\.path$') # Print the excluded set every run so the boundary stays visible. if [ -n "$excluded" ]; then echo "Third-party submodules (excluded from --remote, kept at pinned revision): $excluded"