Skip to content

Separate a package's configured update source from its provenance, and fix channel selection - #42

Merged
line0 merged 14 commits into
mainfrom
channel-fixes
Aug 20, 2026
Merged

Separate a package's configured update source from its provenance, and fix channel selection#42
line0 merged 14 commits into
mainfrom
channel-fixes

Conversation

@line0

@line0 line0 commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Splits a package's configured update source from the record of where its installed copy actually came from, and makes channel selection deterministic — the two halves of a CI mishap that published DependencyControl's internal main channel as a second default and left installs tracking a channel that never receives releases.

  • configuredSource vs currentSource — the former holds the source and channel the next update should use, the latter records the completed install, stamped with the feed URL so provenance survives a later change to the declared feed or a userFeed override. Configs migrate to schema v0.9.0 automatically, and installs stranded on main move to stable.
  • Channel selection — an explicitly requested channel now wins over the recorded one, a channel the feed no longer offers falls back to the default unless the source is pinned, and a feed flagging several defaults is reported rather than resolved by table order. merge-feed leaves exactly one channel flagged, so a publish can't create that state again.
  • Release CI — the first publish to a new publish branch seeded it from the released commit's tree, dev feed included, which is where the second default came from. It now hands the merge an empty feed to fill.
  • ConfigView.setFile — bound a view to a file while leaving its hive in the previous handler's tree, so a save wrote nothing and reported success; a freshly installed package therefore recorded no source at all. It now moves the hive across and reads the file unless the caller opts out.
  • SemanticVersion — a range written >= 1.2.3, with a space after the operator, parsed to an interval no version satisfies and returned false for everything without an error.
  • Toolbox — a new Package Source Configuration macro to set the source kind, channel, stickiness and feed/provider per package, and the install/update/uninstall pickers now show each package's channel.

The published feed still needs main republished once at v0.9.0, so installs stuck on it pick up the migration before that channel is retired.

🤖 Generated with Claude Code

@github-actions

github-actions Bot commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Test results — macos-latest ✅1114 · ubuntu-latest ✅1114 · windows-latest ✅1111

Summary

Tests 📝 Passed ✅ Failed ❌ Skipped ⏭️ Other ❓ Flaky 🍂 Duration ⏱️
3342 3339 0 3 0 0 1m 45s

🎉 No failed tests in this run.

Github Test Reporter by CTRF 💚

🔄 This comment has been updated

line0 added 13 commits August 20, 2026 23:13
…m its provenance

`currentSource` served as both the source the next update should use and the one
the installed copy came from, so a source change was indistinguishable from an
update that had already applied it. `configuredSource` now holds the intent while
`currentSource` records the install, stamped with the feed URL it came from so
provenance survives a later change to the declared feed or a userFeed override.
Existing configs migrate to schema v0.9.0.

Channel selection reads from those same records: an explicitly requested channel
wins over the recorded one, a channel the feed no longer offers falls back to the
default unless the source is pinned, and a feed marking several channels as the
default is reported instead of resolved by table order. merge-feed leaves exactly
one channel flagged, so publishing can't produce that state in the first place.
…g nothing

A comparator group is split on whitespace, so `>= 1.2.3` became a bare `>=` and a
bare version; the operator token parsed as a wildcard and reduced the group to an
interval no version falls in, returning false for every input with no error to
show for it. node-semver accepts that spacing, so an operator is joined to the
version following it before the group is tokenized. Hyphen ranges are matched
before that step and keep their spaces.
A package installed for the first time recorded no update source in the config
file — neither the source configured for it nor where the installed copy came
from — so its next update check had nothing to go on. Its record binds the shared
config file through setFile, which swapped the view's handler but left its hive in
the old handler's tree. A save on the new handler never finds it there, so it
wrote nothing and reported success. setFile now moves the hive across.

It also deferred the file read, as it had since 2015 when it only recorded a path
and left the constructor to load. A handler it creates today starts on an empty
tree, so a view bound that way sits on an empty hive that the next save writes
over the file's entry. It now reads the file unless the caller passes noLoad, and
mergeHive fails a save whose view has no hive in the tree at all.
The refresh writes its output only where it changed something, and serve-updates
loaded that output unconditionally, so a feed already in sync with the working
copy died on a file that was never written. It now serves the source feed in that
case, which is what the refreshed copy would have contained.
… to serve multiple channels simultaneously
…same hive

A save merged the hive the handler happened to hold, so every key another writer
had changed in it since that handler last read the file went back to the older
value. Two handlers over one file is not exotic: a self-update reloads the module
and leaves the old and new instances with a tree each, and two Aegisub instances
have one apiece by definition.

Each handler now keeps the file's contents as of its last load or save and writes
the difference against it, so a field it never touched keeps whatever the file
holds. A hive dropped from the tree is still dropped from the file, but only by a
handler that actually held it.
A record writes its config when loading finds it out of sync, and that check is
the generic import of scriptFields. `version` left that list when it became a
computed property persisted as a string, so a release that changed nothing else
was never noticed and the recorded version stayed behind. DependencyControl's own
entry is where it shows, since the instance that finishes its update is the old
one and no later load corrected it.

Compare the recorded version against the declared one directly, as the packed-int
field in scriptFields used to.
…until reload

FeedLoader copied the fetch policy (blockPrivateHosts, feed size and
timeout caps) into fields at construction, so disabling private-host
blocking mid-session left feed fetches refusing loopback URLs for the
rest of the session. The policy is now read from the config each time
a feed is built, as package-file downloads already did.
@line0
line0 merged commit 6b6bd57 into main Aug 20, 2026
4 checks passed
@line0
line0 deleted the channel-fixes branch August 20, 2026 23:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant