Skip to content

Latest commit

 

History

History
120 lines (99 loc) · 28.9 KB

File metadata and controls

120 lines (99 loc) · 28.9 KB

Changelog

All notable changes to this project are documented in this file.

[0.6.0] - 2026-10-07

  • change nuget: every package shows its source next to its version, (7.0.0, nuget.org): the source it was restored from (recorded in its .nupkg.metadata) or, if it is not restored yet, the sources it would come from (nuget.config with packageSourceMapping, or the project's RestoreSources/RestoreAdditionalProjectSources). When sources other than nuget.org are involved, a legend maps source names to URLs. The summary counts packages per source, and --markdown has a Source column.
  • [BUG] clean --delete -o: --delete deletes immediately and --output-file writes a script to run later, but the two together silently deleted and wrote no script. The combination is now rejected; pass one of them, or -i to pick and write the script (an explicit -i makes the picker write the script even next to --delete).
  • [BUG] clean --non-current: the flag never reached obj, so bld clean --non-current --obj deleted even the current target's obj/<Configuration>/<tfm>. It now marks only the obj TFM directories no project targets, keeps the current output and the restore artifacts (so it implies --keep-assets), and leaves publish, package and TestResults alone, since they have no TFM to be stale by.
  • [BUG] clean/stats: a project with AppendTargetFrameworkToOutputPath=false and a RuntimeIdentifier builds into bin/<Configuration>/<rid>/ without a TFM segment, which the output check reported as output layout not recognized. The RID directory under a configuration directory is now recognized and cleaned like the configuration directory; --non-current treats it as current.
  • [BUG] clean: after deleting a TFM directory the now-empty parents (bin/Debug, obj/Debug) were left behind. They are removed up to, not including, the bin/obj (or artifacts/<kind>/<project>) root and never above the project directory; the generated scripts get matching rmdir lines (rmdir /q ... 2>nul on Windows, rmdir -- ... 2>/dev/null || : on Linux/macOS) that remove a parent only while it is empty.
  • [BUG] clean/stats: the size and file count followed symlinks and junctions out of bin/obj, counting a share's contents and risking a cycle, and the picker, the script preview and the stats table used three slightly different copies that could disagree on an unreadable directory. One measurement that skips reparse points is shared by all three.
  • [BUG] tfm: the dry run listed projects whose TargetFramework comes from Directory.Build.props, an import or a conditional PropertyGroup as migrations, and --apply then failed each of them with "No unambiguous " and exit 1. Such projects are now listed under "Not migratable by bld" with the reason, in the dry run and with --apply alike, and do not count as failed migrations; the exit code is 1 only when a rewritable project was not written or nothing at all could be rewritten.
  • [BUG] tfm: without network the end-of-life check came back empty, so no framework was flagged or dropped and the run looked clean. A list built into the release (as of 2026-10-02) is used instead, with a warning naming that date; live data still wins.
  • [BUG] tfm: the automatic --from detection searched with a depth of 4 regardless of --depth, because it built its own options. It now uses the parsed options; the CleaningOptions defaults match the command-line defaults (depth 3, obj off).
  • [BUG] nuget: a PackageReference with VersionOverride under central package management was reported at the central version. The override is what restore uses and is reported now.
  • [BUG] nuget: version constraints in the rules file were compared as System.Version with a hand-made prerelease adjustment that put 2.0.0-beta somewhere in 1.x, so >=2.0.0-beta never matched the beta it named and four-part versions compared wrongly. Constraints use NuGet version ordering now; the file format is unchanged and finally documented.
  • [BUG] nuget: an exception escaping the package extractor (categorizer, assets reader) was printed but did not reach the error sink, so the run exited 0.
  • [BUG] cpm --overwrite: new PackageVersion entries went into the first ItemGroup that already had some, even when that group carried a Condition (a per-framework pin block), so they were only active for that framework. They go into the first unconditioned group now.
  • [BUG] build-props --list: the import tree nested files by path length, so two Directory.Build.props without an ancestor relationship (a sibling directory, an explicit import) rendered as parent and child. The tree follows directory ancestry.
  • change nuget: a package matched by the rules file's # blacklist (and not by # whitelist) is its own category, "Blacklisted", shown first in the per-project, aggregated and transitive views instead of staying "Microsoft Official" because of its prefix.
  • [BUG] outdated: a saved package policy could loosen a stricter --max-bump given for the run (--max-bump patch with a minor policy produced a minor bump). The effective cap is now the more restrictive of the policy rule and --max-bump; --max-bump-for still overrides both.
  • [BUG] outdated: Ctrl+C during --apply was reported as a failed file write and the run went on to exit 1; the project, props and PackageDownload writers no longer swallow the cancellation, so the run exits 130.
  • [BUG] outdated: the NuGet request throttle counted the wait for a slot against the request timeout, so a long queue in front of a slow feed timed requests out before they were sent. The timeout starts when the request is sent.
  • [BUG] outdated: non-CPM projects that pin the same package at different versions were shown with only the lowest pin. Every distinct pin is listed in the current cell (9.0.0 (1), 8.0.1 (3)) and each project is updated from its own pin.
  • [BUG] containerize --migrate: USER $APP_UID, the line every .NET 8+ template Dockerfile carries, was written verbatim as ContainerUser=$APP_UID (an image with a non-existent user) and APP_UID was listed as an unresolved build arg. $APP_UID/${APP_UID} are the SDK's default app user: nothing is written and a note says so.
  • [BUG] containerize --migrate: a plain dry run (no --apply, -i or --run-as-root) asked "Keep running as root?" for a Dockerfile without USER although it writes nothing. Only a run that writes asks; the dry run notes that --apply would.
  • [BUG] containerize: heredocs in RUN/COPY/ADD (<<EOF, <<-EOF, <<"EOF") were read line by line, so an EXPOSE inside the body became a ContainerPort. The body up to the terminator is skipped; the instruction itself is handled as before.
  • [BUG] containerize --validate: ContainerRepository names with __ or repeated - (my__app, my--app) and names with a registry host and port (registry:5000/team/my-app) were reported as invalid although Docker accepts them.
  • change containerize: the -p short alias of --projects is gone (it collided with --package/-p in outdated), --force is now --allow-unsupported (--force still works as an alias), the never-used --concurrency option is no longer offered, --markdown with --interactive is rejected instead of silently printing text, and --apply, --delete-dockerfile, --allow-unsupported or --run-as-root without --migrate (or --validate for --apply) warn that they have no effect.
  • [BUG] clean picker: at a very narrow width a path under a Windows drive root (C:\) was shortened to C:\...in, cutting the directory name; the leaf is kept and the root dropped instead.
  • change clean: --yes/-y are aliases of --force, so "do not ask" is spelled the same as in outdated undo.
  • change: the NuGet User-Agent is bld/<version> instead of NugetMetadata/1.0.0.
  • change: the stats help texts no longer say "clean"; the README documents --confirm as the working option it is, lists the exit codes (0, 1, 130), the shared dry-run/--apply/--interactive model, the extra picker keys (k/j/h/l, q), and that outdated undo takes only --root and --log.
  • build: the CI workflow runs the tests on Windows as well as Linux and cancels superseded runs. The unreachable Visual Studio MSBuild registration path (VSService, never wired to an option) is removed; Visual Studio still contributes its targets through VSToolsPath.

[0.5.0] - 2026-09-26

  • [BUG] clean --non-current: a project whose current output sits directly in bin/<Configuration> (legacy projects, AppendTargetFrameworkToOutputPath=false) had that whole directory marked, deleting the output --non-current promises to keep. Only TFM directories below it that no project targets are marked now.

  • [BUG] clean -o on Windows: the script relied on setlocal disabledelayedexpansion, which does not stop cmd from expanding %VAR%, so a path containing %TEMP% deleted another directory. % is written as %%.

  • [BUG] cpm --apply: a package conditional in one project and unconditional in another was centralized and kept its inline Version in the unconditional one, and any conditional reference kept its inline Version too - both NU1008, restore failed. Per-framework pins, property references ($(FooVersion)), ranges and floating versions now become VersionOverride; only literal unconditional versions move to Directory.Packages.props.

  • [BUG] cpm: a Directory.Packages.props inherited from a parent directory was ignored and a new one created next to the solution, which shadowed it and left the packages it managed without a version (NU1010). The inherited file is the one merged into (with --overwrite).

  • [BUG] outdated --apply: conditional PackageVersion entries in Directory.Packages.props were rewritten to the one version proposed for the package, e.g. moving a net48 pin to a version without net48. They are skipped with a warning, as conditional PackageReference items already were.

  • [BUG] tfm: platform target frameworks (net8.0-windows, -android, -ios) could not be migrated, and the dry run and --apply disagreed about them. They migrate now and keep their platform: --to net10.0 turns net8.0-windows into net10.0-windows. Switching platforms is not treated as a migration.

  • [BUG] containerize: the directory scan found only files named exactly Dockerfile; Dockerfile.prod and api.Dockerfile were skipped (Dockerfile.dockerignore is still not one). When several Dockerfiles build one project, the plain Dockerfile is the one migrated.

  • [BUG] containerize --migrate: a relative WORKDIR replaced the previous one instead of resolving against it (/app then bin is /app/bin); a first relative one is noted, since it depends on the base image.

  • [BUG] outdated: Ctrl+C was ignored during project evaluation and the NuGet lookups, and a cancelled lookup was reported as a failed fetch. Cancellation now stops the run; every command exits with 130 and "Cancelled." on Ctrl+C.

  • [BUG] clean -o on Linux/macOS: the script was created 0644 (only an overwritten one kept an execute bit it already had), so ./clean.sh failed with "Permission denied" more often than not. It is now created executable (0755 minus the umask; an existing file gains the execute bits), starts with #!/bin/sh, keeps going past an rm that fails and exits non-zero at the end instead of reporting success, uses rm -rf --/rm -f --, and bld prints how to run it.

  • change: --markdown is only offered by the commands that print tables (stats, nuget, containerize, outdated, tfm, build-props); clean, cpm and outdated undo accepted it and ignored it, and now reject it.

  • extend clean command: --keep-private-packages keeps a cloned repo restorable after losing access to its private feeds. Before anything is cleaned, every package the projects restored (per project.assets.json, transitive ones included) from a source other than nuget.org - as recorded in the package's .nupkg.metadata - is copied, unless the file carries nuget.org's repository signature and so came through a mirror unchanged, from the packages folder into .nuget-private/<source key>/, and nuget.offline.config is written next to the root: nuget.org as configured, each private source's key pointing at its folder, no credentials, package source mapping carried over. dotnet nuget locals all --clear then loses nothing that dotnet restore --configfile nuget.offline.config cannot bring back. --private-packages-dir picks another folder.

  • change clean: the picker is the default. bld clean now opens it and deletes what is checked after one confirmation; the deletion script is only written with --output-file/-o, whose path is now optional (a bare -o writes clean.cmd/clean.sh). An explicit --delete deletes without the picker as before, and an explicit -i brings the picker back next to --delete or -o. Without a terminal, clean fails and names --delete and -o instead of writing a script nobody asked for.

[0.4.3] - 2026-09-22

Changes

  • change containerize --migrate: a custom ENTRYPOINT is written as ContainerAppCommand items with ContainerAppCommandInstruction=Entrypoint and CMD as ContainerDefaultArgs, instead of the ContainerEntrypoint items the SDK deprecated in .NET 8. A variant of the image the SDK would pick anyway (aspnet:8.0-alpine, 8.0-noble-chiseled) becomes ContainerFamily rather than a pinned ContainerBaseImage, so the variant stays and the version follows the target framework; Windows tags stay pinned. A Dockerfile without USER ran as root while the SDK defaults to the non-root app user on .NET 8+ images: the command now asks per Dockerfile whether to keep root and writes ContainerUser=root on yes; --run-as-root answers yes for all, and without a terminal the SDK default is kept and noted. The "already has container settings" check covers every SDK property (ContainerRegistry, ContainerImageTag(s), ContainerRuntimeIdentifier(s), ContainerImageFormat, ContainerArchiveOutputPath, LocalRegistry, ...).
  • extend containerize command: --validate checks the SDK container settings every project carries and, with --apply, rewrites the ones with an exact equivalent: a ContainerBaseImage that is the image the SDK computes anyway is removed, a family variant of it (runtime-deps:10.0-azurelinux3.0-distroless-extra) becomes ContainerFamily (azurelinux3.0-distroless-extra), the obsolete ContainerImageName becomes ContainerRepository, deprecated ContainerEntrypoint items become ContainerAppCommand with ContainerAppCommandInstruction=Entrypoint when the instruction is None, and a ContainerWorkingDirectory of /app goes. Reported without a fix: elements with the Container prefix the SDK does not read, a ContainerFamily next to a ContainerBaseImage, a pin of another repository or version than the SDK would pick, invalid ContainerAppCommandInstruction/ContainerImageFormat/LocalRegistry values, tags and repository names the registry would reject, ContainerImageTag next to ContainerImageTags, bad ContainerPort items, ContainerRuntimeIdentifiers outside RuntimeIdentifiers, and a pin carrying a platform suffix (aspnet:8.0-amd64): ContainerFamily cannot carry the platform, so dropping the pin would hand the architecture to the RuntimeIdentifier and let the image follow the building machine. Conditioned settings are reported, never rewritten.
  • extend containerize command: --interactive/-i with --migrate or --validate asks instead of --apply. Per Dockerfile: migrate anyway when the SDK cannot express part of it (default no), write the settings (default yes), delete the Dockerfile and the Visual Studio container-tools settings (default no; --delete-dockerfile answers yes for all). Per fixable validation finding: fix it (default yes). Answers are written at once; without a terminal the command fails with a hint.
  • [BUG] containerize --projects: the scan read ContainerImage, which is not an SDK property, so a project identified by ContainerRepository, ContainerFamily, ContainerRegistry or ContainerImageTag(s) alone was not listed. Those are read now (ContainerImageName as the obsolete alias of ContainerRepository) and shown.
  • [BUG] clean/stats: a project with a RuntimeIdentifier builds into bin/<Configuration>/<tfm>/<rid>/, which the output check did not know, so the project was skipped with output layout not recognized. The RID directory is now recognized and the project's bin/<Configuration>/<tfm> cleaned like any other; --non-current treats its framework as current.
  • change clean --interactive/-i: the picker now deletes. It used to write a deletion script unless --delete was also given, which made the obvious command a dry run. After enter it asks once for the whole selection (Delete 12 directories (340.2 MiB)?, default no) and then deletes it; --force skips that question, an explicit --confirm still adds the per-directory ones, and --output-file/-o writes the script for the selection instead.

[0.4.2] - 2026-09-19

Changes

  • extend clean command: --interactive/-i shows everything the run would delete as a list grouped by project, with the same keys as the outdated picker. b/o/p/g/t toggle bin, obj, publish, package and test results for every project on the top line or for one project on its line, space toggles a directory, headers show each category as checked, unchecked or partly checked with the selected and total size. --obj, --publish and --test-results only decide what starts out checked. With --delete the picker replaces the per-directory confirmation unless --confirm is given. Every row shows the fully qualified path that would be deleted, cut in the middle when the terminal is too narrow; the same directory marked with and without a trailing separator is one row, not two; and what the picker hands back is checked against what the run marked, so a path the run never marked aborts it instead of being deleted.
  • extend clean and stats: --test-results also cleans TestResults/ next to each project (VSTestResultsDirectory when set) and next to its solution.
  • [BUG] clean/stats --publish: a PackageOutputPath outside the build output (a local NuGet feed, artifacts/package/<config>/) was marked as a whole, with every other project's packages in it. Package output is now cleaned per file: only <PackageId>.<version>[.symbols].nupkg/.snupkg directly in that directory, only for projects that pack, and the directory itself is never touched. The name only nominates a file; the id in the package's own .nuspec decides, so Foo cannot delete Foo.1's Foo.1.2.0.nupkg, and a file that does not read as a package is left alone. The scripts get one del/rm line per file, --delete asks per file at the Directory confirm level, and the picker lists each file with its own size.

[0.4.1] - 2026-09-18

Changes

  • [BUG] outdated --interactive and outdated undo -i: the picker showed at most 30 rows, leaving the rest of a tall terminal empty. It now uses the whole height, less the header lines as they wrap at the terminal width.

[0.4.0] - 2026-09-18

Changes

  • extend outdated command: bld outdated undo. Every --apply, --interactive and tfm --update-packages run that changes a file records what it wrote (file, package, value before and after, per element) under BLD_HOME/history, and undo takes the newest run back after showing a table and asking once — or part of it with -p/--exclude, or from a grouped picker with -i. Reverted edits leave the record, so a second undo pops the run before; --list and --run <n> reach older runs. A value that no longer reads as the run left it is skipped and named (with the later run that wrote it, when there is one), never overwritten. --yes skips the question, --verify-restore restores afterwards.
  • extend tfm command: --update-packages now runs outdated --apply on the same input after the frameworks are written, so packages are checked for compatibility with the new target framework, capped by the new --max-bump option and the saved package policies, checked for dependency conflicts, and updated in Directory.Packages.props under central package management. It used to bump every inline PackageReference to the latest stable version without any of that, and updated nothing under central package management.
  • [BUG] exit codes: nuget and build-props returned 0 after reporting errors; they now return 1 when a solution or project could not be analyzed (build-props also when a project could not be evaluated). cpm and tfm collected solution and project load errors and never showed them; they are now printed and fail the command.
  • build: TreatWarningsAsErrors is set (the previous WarningsAsErrors property takes a list of warning ids, so warnings never failed the build); a repo nuget.config pins the restore to nuget.org. A CI workflow runs the test suite on every push and pull request, and the publish workflow runs it before packing and refuses a release tag that does not match the project version.
  • extend outdated command: --interactive shows the packages grouped, so a whole family can be toggled in one keystroke, and left/right move a package between the versions the feeds offer for it — highest patch, highest minor, highest major — or cap a whole family at a bump class from its group line, which shows the class (a package with nothing at or below the cap is unchecked until the cap admits it again); packages are indented under their group line. --group-by <prefix|bump|none> (default prefix), --group-depth and --group-min control the grouping; passing --group-by explicitly also groups the report table. Every row names its bump class (patch/minor/major, colored), --preselect <all|no-major|patch|none> picks what starts out checked, versions the bump cap held back are listed unchecked instead of being unreachable, and esc cancels without writing. [BUG] --interactive without a terminal threw out of Spectre; it now fails with an exit code and a hint.
  • extend outdated command: one lookup per package now collects the highest compatible version per bump class instead of stopping at the first usable one, so --max-bump picks from a set rather than constraining the fetch. [BUG] the version in the held column was never checked for target framework compatibility; every offered version is now checked, and registration pages that end below the pinned version are no longer fetched.
  • extend outdated command: --max-bump-for "<pattern>=<level>" overrides --max-bump per package pattern, and --interactive prints the equivalent prompt-free command line after the selection.
  • extend outdated command: the dependency check also runs in reverse. A package that stays where it is and declares a range on one being updated now holds that update back when the new version falls outside the range (A 9.0.1 breaks Q 3.1.0, which requires A [8.0.0, 9.0.0)); --interactive offers to include the blocking package's update instead. The manifests come from the registration data already fetched, so it costs no extra request. [BUG] a range declared for one target framework was dropped when another framework group declared an open-ended range on the same dependency, so an upper bound could go unchecked.
  • extend outdated command: package policies. policy.json under BLD_HOME (or the local application data folder) holds persistent per-pattern caps (MassTransit* at most minor) that every run applies; --max-bump-for overrides a policy, --ignore-policy skips them. In --interactive, p saves a "no major" rule for the package under the cursor (the family's pattern on a prefix group line) and caps the row, or removes the rule it has. The held-back summary names the source of each cap.
  • [BUG] outdated --interactive: the picker was not drawn until the first key was pressed; only the "Interactive update selection" rule was visible.
  • speed up outdated: ProjectReferences are read from the same MSBuild evaluation as the PackageReferences (they used to cost a second, sequential evaluation of every project configuration), only the Release configuration is evaluated, evaluations reuse pooled ProjectCollections and one shared evaluation context so the SDK imports are parsed once per worker and the SDK is resolved once per run instead of once per project, the SDK's default item globs are not expanded (nothing read depends on them), every package source is queried at once, all registration pages that can hold a candidate are fetched at once, and lookups run at least 16 wide regardless of --concurrency. -v Info prints how long evaluation and metadata fetching took.
  • extend outdated command: --eval-cache skips the MSBuild evaluation of a project configuration when the project file and every non-SDK import are byte-identical to the last evaluation (SHA-256, keyed by global properties, MSBuild version and which Directory.Build.* files exist up the tree). Entries live under BLD_HOME or the local application data folder. Opt-in: properties set through environment variables are not detected.

[0.3.0] - 2026-09-12

Changes

  • [BUG] clean/stats: projects using the SDK artifacts layout (UseArtifactsOutput=true) were only cleaned when single-targeted; multi-targeted output under artifacts/bin/<project>/<config>_<tfm>[_<rid>]/ was skipped without any message. Both are now recognized, --non-current applies to the TFM segment, and an OutDir that matches no known layout is reported as a warning.
  • extend clean/stats: --publish also cleans publish output (PublishDir) and pack output (PackageOutputPath), including artifacts/publish/<project>/ and artifacts/package/ in the artifacts layout. Off by default.
  • extend tfm command: warns when the governing global.json pins an SDK that cannot build the target framework; --update-global-json sets sdk.version to the highest installed SDK of the target's major on --apply.
  • extend nuget and outdated: GlobalPackageReference and PackageDownload items are listed and checked. [BUG] a GlobalPackageReference was attributed to the SDK's NuGet.targets instead of Directory.Packages.props, so outdated --apply reported the update but wrote nothing; it is now updated in place. PackageDownload versions are updated in their bracketed form and skip the TFM check.
  • extend nuget command: --transitive lists the packages resolved through other packages (from project.assets.json), categorized and blacklist-checked like direct references, with the packages that pull them in.
  • extend outdated command: package sources come from the nuget.config hierarchy (enabled sources, packageSourceMapping, packageSourceCredentials) instead of only api.nuget.org, so packages on private feeds are checked and updated. --source restricts or overrides the sources, --ignore-source-mapping queries every source. tfm --update-packages uses the same sources.
  • extend outdated command: --max-bump <major|minor|patch> caps how far a package may move from the version it is pinned at now. Versions above the cap are reported in a new held column instead of being applied.
  • extend outdated command: --package/-p and --exclude select a subset of packages by wildcard pattern.
  • extend outdated command: --apply and dry runs now check the packages they would update against the versions their dependencies will end up at, and hold back any package whose declared range would be violated. --allow-conflicts updates anyway. The check covers direct references only.
  • extend outdated command: --verify-restore runs dotnet restore after --apply and fails the command on NuGet errors.
  • extend containerize command: --migrate turns each Dockerfile into SDK container properties on the project it builds (ContainerBaseImage, ContainerPort, ContainerEnvironmentVariable, ContainerLabel, ContainerWorkingDirectory, ContainerUser, ContainerEntrypoint/ContainerDefaultArgs with ContainerAppCommandInstruction). The runtime stage and the stages it derives from are folded like Docker does, ARG/ENV values are substituted, and settings that equal the SDK's defaults (base image for the target framework, /app, dotnet App.dll) are left out so they keep following the project. Instructions the SDK cannot express (RUN, VOLUME, HEALTHCHECK, files copied from the build context) are listed and block the Dockerfile unless --force. Dry run by default; --apply writes with the file's layout preserved, --delete-dockerfile also removes the Dockerfile and the Visual Studio container-tools properties and package. --markdown tabulates the plan.
  • bump System.CommandLine to 2.0.12 and Microsoft.SourceLink.GitHub to 10.0.401.

[0.2.33] - 2026-05-25

Changes

  • remove net8.0 target
  • extend outdated command: --orphaned and --interactive
  • fix System.Text.Json load error by preloading

[0.2.32] - 2026-03-29

Changes

  • Added build-props command to the root command set.
  • [BUG] .slnf file handling changed. Actually applies the fiter from the .slnf file now (it did not before).