Skip to content

Reconcile incoming Windows software upgrade_codes with those in existing correlated software_titles - #36175

Merged
jacobshandling merged 6 commits into
mainfrom
write-new-upgrade-code-to-existing-titles-without-one
Nov 25, 2025
Merged

Reconcile incoming Windows software upgrade_codes with those in existing correlated software_titles#36175
jacobshandling merged 6 commits into
mainfrom
write-new-upgrade-code-to-existing-titles-without-one

Conversation

@jacobshandling

@jacobshandling jacobshandling commented Nov 22, 2025

Copy link
Copy Markdown
Contributor

Related issue: Resolves #35724

This PR addresses Issue 3 in the above unreleased bug: when a new software comes in, its upgrade_code is compared with that of any corresponding software_titles, and reconciled appropriately - see code for the various cases

Testing

  • Added/updated automated tests
  • QA'd all new/changed functionality manually
  • Load tests: @AndreyKizimenko since I don't think you've load tested the original story yet, this change will be covered by those tests when you do

Summary by CodeRabbit

  • Bug Fixes

    • Ensured upgrade_code values on existing software titles are consistently updated when incoming data provides non-empty values; added logging and safe retry handling for anomalous states.
  • Tests

    • Added table-driven tests covering upgrade_code reconciliation scenarios (empty, non-empty, conflicting, NULL) and verification of resulting title values.
  • Refactor

    • Introduced a lightweight software title summary type and updated internal mappings to streamline pre-insert and reconciliation processing.

✏️ Tip: You can customize this high-level summary in your review settings.

@jacobshandling

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Nov 22, 2025

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai

coderabbitai Bot commented Nov 22, 2025

Copy link
Copy Markdown
Contributor

Walkthrough

Adds a lightweight SoftwareTitleSummary type and a reconciliation routine that updates existing software_titles' upgrade_code when incoming software (matched by checksum) supplies a non-empty upgrade_code; the reconciliation is invoked in Phase 1 of applyChangesForNewSoftwareDB before pre-insert and will halt Phase 1 on error. Tests exercise update/skip/conflict/NULL scenarios.

Changes

Cohort / File(s) Summary
Datastore reconcile + integration
server/datastore/mysql/software.go
Added reconcileExistingTitleEmptyUpgradeCodes and related helper types/paths; invoked from applyChangesForNewSoftwareDB Phase 1 pre-insert step; updated internal signatures to use SoftwareTitleSummary where appropriate.
New tests for upgrade_code reconciliation
server/datastore/mysql/software_upgrade_code_test.go
Added TestUpdateSoftwareTitlesUpgradeCode table-driven tests covering: updating empty upgrade_code, skipping when incoming empty, logging/skipping on conflicting non-empty, and replacing NULL with non-empty.
API model addition
server/fleet/software.go
Added exported SoftwareTitleSummary struct (ID, Name, Source, ExtensionFor, UpgradeCode, BundleIdentifier) used to map existing titles during ingestion.

Sequence Diagram

sequenceDiagram
    participant Apply as applyChangesForNewSoftwareDB
    participant Recon as reconcileExistingTitleEmptyUpgradeCodes
    participant DB as MySQL

    Apply->>Recon: Call with incomingSoftwareByChecksum\nand incomingChecksumsToExistingTitleSummaries
    Recon->>DB: Query existing titles for checksums
    Recon->>Recon: Build list of title pairs\n(oldUpgradePtr, newUpgradePtr)
    alt incoming upgrade_code non-empty & existing empty/NULL
        Recon->>DB: Retry-wrapped UPDATE upgrade_code
        DB-->>Recon: OK
    else incoming upgrade_code empty
        Recon-->>Apply: Skip update (no-op)
    else existing non-empty different from incoming
        Recon-->>Recon: Log warning, skip
    end
    Recon-->>Apply: Return success or error (errors halt Phase 1)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20–30 minutes

  • Review transaction/retry and SQL update paths in server/datastore/mysql/software.go.
  • Confirm correct mapping/use of SoftwareTitleSummary vs SoftwareTitle.
  • Validate test coverage and DB setup/teardown in software_upgrade_code_test.go.

Possibly related PRs

Suggested labels

#g-software, bug

Suggested reviewers

  • sharon-fdm
  • cdcme
  • getvictor

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Description check ⚠️ Warning The description includes the related issue (#35724), explanation of the reconciliation logic, and mentions testing performed. However, it does not check many of the template boxes for database migrations, collation, data validation, and other required items. Complete the PR description template by checking applicable boxes and providing details on data validation, SQL injection prevention, database migration considerations, and QA specifics.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and accurately describes the main change: reconciling incoming Windows software upgrade_codes with existing software_title records.
Linked Issues check ✅ Passed The PR implements the reconciliation logic required by issue #35724 to normalize upgrade_code values across software, software_titles, and host views.
Out of Scope Changes check ✅ Passed All changes are directly related to the reconciliation of upgrade_code values for software titles as specified in the linked issue #35724.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch write-new-upgrade-code-to-existing-titles-without-one

📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3770a29 and 7a3aefa.

📒 Files selected for processing (3)
  • server/datastore/mysql/software.go (14 hunks)
  • server/datastore/mysql/software_upgrade_code_test.go (1 hunks)
  • server/fleet/software.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • server/datastore/mysql/software_upgrade_code_test.go
🧰 Additional context used
📓 Path-based instructions (1)
**/*.go

⚙️ CodeRabbit configuration file

When reviewing SQL queries that are added or modified, ensure that appropriate filtering criteria are applied—especially when a query is intended to return data for a specific entity (e.g., a single host). Check for missing WHERE clauses or incorrect filtering that could lead to incorrect or non-deterministic results (e.g., returning the first row instead of the correct one). Flag any queries that may return unintended results due to lack of precise scoping.

Files:

  • server/fleet/software.go
  • server/datastore/mysql/software.go
🧠 Learnings (2)
📚 Learning: 2025-07-08T16:13:39.114Z
Learnt from: getvictor
Repo: fleetdm/fleet PR: 30589
File: server/datastore/mysql/migrations/tables/20250707095725_HostIdentitySCEPCertificates.go:53-55
Timestamp: 2025-07-08T16:13:39.114Z
Learning: In the Fleet codebase, Down migration functions are intentionally left empty/no-op. The team does not implement rollback functionality for database migrations, so empty Down_* functions in migration files are correct and should not be flagged as issues.

Applied to files:

  • server/datastore/mysql/software.go
📚 Learning: 2025-09-14T21:42:42.400Z
Learnt from: getvictor
Repo: fleetdm/fleet PR: 32960
File: ee/server/scim/scim_otel_test.go:118-121
Timestamp: 2025-09-14T21:42:42.400Z
Learning: In Fleet's OTEL tests, avoid using global tracer providers (otel.SetTracerProvider) when not necessary, as it can make tests flaky when run in parallel. Prefer dependency injection or context-based tracing when possible. Only use global providers when the code under test specifically depends on global state.

Applied to files:

  • server/datastore/mysql/software.go
🧬 Code graph analysis (1)
server/datastore/mysql/software.go (1)
server/fleet/software.go (3)
  • SoftwareTitleSummary (233-248)
  • Software (49-115)
  • Software (117-119)
🔇 Additional comments (2)
server/fleet/software.go (1)

230-248: SoftwareTitleSummary shape and tags look consistent with SoftwareTitle.

The subset of fields, pointer usage for optional identifiers, and db/json tags all align with SoftwareTitle; this helper type is well-structured for the datastore reconciliation logic.

server/datastore/mysql/software.go (1)

819-887: Host software title pre-insert and listing SQL correctly propagate upgrade_code.

The updates to:

  • preInsertSoftwareInventory (using SoftwareTitleSummary, selecting bundle_identifier, upgrade_code, and matching titles via (COALESCE(bundle_identifier, name), source, extension_for, COALESCE(bundle_identifier, ''))), and
  • The host software listing SELECTs that:
    • Project software_titles.upgrade_code as the title-level upgrade_code,
    • Aggregate software.upgrade_code into software_upgrade_code_list,

look consistent and scoped:

  • All new SELECTs are still constrained either by checksum/title groups or host/team filters, so they don't broaden result sets.
  • GROUP BY clauses include the new non-aggregated upgrade_code columns, avoiding SQL ambiguity.
  • Title insert and subsequent SELECT/ID-mapping paths remain deterministic.

No functional issues found here.

Also applies to: 958-968, 991-1004, 5253-5368

Tip

📝 Customizable high-level summaries are now available in beta!

You can now customize how CodeRabbit generates the high-level summary in your pull requests — including its content, structure, tone, and formatting.

  • Provide your own instructions using the high_level_summary_instructions setting.
  • Format the summary however you like (bullet lists, tables, multi-section layouts, contributor stats, etc.).
  • Use high_level_summary_in_walkthrough to move the summary from the description to the walkthrough section.

Example instruction:

"Divide the high-level summary into five sections:

  1. 📝 Description — Summarize the main change in 50–60 words, explaining what was done.
  2. 📓 References — List relevant issues, discussions, documentation, or related PRs.
  3. 📦 Dependencies & Requirements — Mention any new/updated dependencies, environment variable changes, or configuration updates.
  4. 📊 Contributor Summary — Include a Markdown table showing contributions:
    | Contributor | Lines Added | Lines Removed | Files Changed |
  5. ✔️ Additional Notes — Add any extra reviewer context.
    Keep each section concise (under 200 words) and use bullet or numbered lists for clarity."

Note: This feature is currently in beta for Pro-tier users, and pricing will be announced later.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
server/datastore/mysql/software_upgrade_code_test.go (1)

26-27: Consider verifying expected log counts.

The test defines expectedInfoLogs and expectedWarningLogs fields (lines 26-27) and sets them in test cases, but never verifies that the correct number of logs are actually emitted during reconciliation. While the core functionality is tested, validating logging behavior could help catch regressions.

If you'd like to verify logs, you could capture log output during the test and assert the counts match expectations.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 78ee32f and 3770a29.

📒 Files selected for processing (2)
  • server/datastore/mysql/software.go (2 hunks)
  • server/datastore/mysql/software_upgrade_code_test.go (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.go

⚙️ CodeRabbit configuration file

When reviewing SQL queries that are added or modified, ensure that appropriate filtering criteria are applied—especially when a query is intended to return data for a specific entity (e.g., a single host). Check for missing WHERE clauses or incorrect filtering that could lead to incorrect or non-deterministic results (e.g., returning the first row instead of the correct one). Flag any queries that may return unintended results due to lack of precise scoping.

Files:

  • server/datastore/mysql/software_upgrade_code_test.go
  • server/datastore/mysql/software.go
🧠 Learnings (2)
📚 Learning: 2025-08-08T07:40:05.301Z
Learnt from: getvictor
Repo: fleetdm/fleet PR: 31726
File: server/datastore/mysql/labels_test.go:2031-2031
Timestamp: 2025-08-08T07:40:05.301Z
Learning: In fleetdm/fleet repository tests (server/datastore/mysql/labels_test.go and similar), using testing.T.Context() is valid because the project targets a recent Go version where testing.T.Context() exists. Do not suggest replacing t.Context() with context.Background() in this codebase.

Applied to files:

  • server/datastore/mysql/software_upgrade_code_test.go
📚 Learning: 2025-08-08T07:40:05.301Z
Learnt from: getvictor
Repo: fleetdm/fleet PR: 31726
File: server/datastore/mysql/labels_test.go:2031-2031
Timestamp: 2025-08-08T07:40:05.301Z
Learning: Fleet repo targets Go 1.24.5 (root go.mod), which supports testing.T.Context(). Do not flag usage of t.Context() or suggest replacing it with context.Background() in tests (e.g., server/datastore/mysql/labels_test.go Line 2031 and similar).

Applied to files:

  • server/datastore/mysql/software_upgrade_code_test.go
🔇 Additional comments (1)
server/datastore/mysql/software.go (1)

423-427: LGTM! Appropriate placement of upgrade_code reconciliation.

The reconciliation is correctly placed before the pre-insertion step in Phase 1, ensuring upgrade_code consistency before software inventory entries are created. Error handling properly propagates failures to halt the Phase 1 process.

Comment thread server/datastore/mysql/software_upgrade_code_test.go Outdated
Comment thread server/datastore/mysql/software.go Outdated
@codecov

codecov Bot commented Nov 22, 2025

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 91.30435% with 8 lines in your changes missing coverage. Please review.
✅ Project coverage is 65.98%. Comparing base (64e0277) to head (7a3aefa).
⚠️ Report is 28 commits behind head on main.

Files with missing lines Patch % Lines
server/datastore/mysql/software.go 91.30% 3 Missing and 5 partials ⚠️
Additional details and impacted files
@@           Coverage Diff           @@
##             main   #36175   +/-   ##
=======================================
  Coverage   65.97%   65.98%           
=======================================
  Files        2130     2130           
  Lines      181411   181465   +54     
  Branches     7564     7564           
=======================================
+ Hits       119694   119732   +38     
- Misses      50767    50775    +8     
- Partials    10950    10958    +8     
Flag Coverage Δ
backend 67.55% <91.30%> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@iansltx iansltx left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I'm coming in on the tail end of this, but did the software table get modified as part of this, including checksum?

switch {
case existingTitle.UpgradeCode == nil:
level.Warn(ds.logger).Log(
"msg", "Encountered Windows software title with a NULL upgrade_code, which shouldn't be possible. Writing the incoming non-empty upgrade code to the title.",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Is this not allowed to be null because we set it to '' for everything with the source programs in a DB migration?

Feels like we're going to have some customer migrate while the system is up and have cases of these spamming logs anyway. Which means that it at least feels to me like if we want to have blank UpgradeCodes on Windows software we coalesce nil to blank at the application layer and the DB is always either null or filled. Guessing there's a reason you have the blank value in the DB though?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Not allowed to be null because we set it to '' for everything with the source programs in a DB migration?

Right

coalesce nil to blank at the application layer and the DB is always either null or filled. Guessing there's a reason you have the blank value in the DB though?

I'm not sure I'm fully understanding, but yes the idea was that the migration initially sets NULL,
"", or "<upgrade_code>", and any subsequent ingestion of software writes with the same
conditions, so this would be an unexpected state and useful to warn

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I guess my question is why have the empty state at all, as distinct from NULL.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I may have to defer to another reviewer here as it feels like I'm missing context here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

You're right that it's slightly redundant semantically: upgrade_code: "" could also be communicated as upgrade_code: NULL + source: "programs"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

...argh, I could've sworn I wrote this comment on the correct thread. Deleted the erroneous comment and readding in the right place.

@jacobshandling jacobshandling Nov 25, 2025

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

why have the empty state at all, as distinct from NULL.

The thinking also has to do with the fact the spec explicitly wants API responses to have upgrade_code a) omitted for non-Windows software, b) included and empty for Windows software without an upgrade_code, or c) included with the code when it's there

To help keep things clear, I basically implemented these 3 options throughout the stack. I can see that it might make sense to make the db column non-nullable and then make the necessary updates at the application layer based on source + ugprade_code

existingTitlesToUpgradeCodesToWrite[existingTitle.ID] = *sw.UpgradeCode
case existingTitle.UpgradeCode != nil && *existingTitle.UpgradeCode != "" && *sw.UpgradeCode != *existingTitle.UpgradeCode:
// don't update this title
level.Warn(ds.logger).Log(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

If there's a different upgrade code here, seems like this should be a brand new title, not an overwrite to the existing one (or a bail), as we're saying that upgrade codes are a dimension of uniqueness.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yea there is a bit of ambiguity/catch22 going on around this scenario. AFAIK it's only theoretical
at this point and this is the direction the team agreed on, so unless you urgently disagree I'd say
this is something we keep in mind and address later if/as needed

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Understood. Will defer remaining review here to @mostlikelee or the like as I wasn't part of those discussions so am missing context. Happy to review the FMA bits by contrast as I know what's going on there but I'm not being particularly useful here.

Comment thread server/datastore/mysql/software.go Outdated
Comment thread server/datastore/mysql/software_upgrade_code_test.go
@jacobshandling

Copy link
Copy Markdown
Contributor Author

did the software table get modified as part of this, including checksum?

@iansltx yep

// though possible for a Windows software to have the empty string upgrade code, would provide no
// additional signal of uniqueness, so omit
if s.UpgradeCode != nil && *s.UpgradeCode != "" {
cols = append(cols, *s.UpgradeCode)
}

@iansltx iansltx left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for the changes! Since I don't have software ingestion in my head at this point I don't feel like I'm in a position to vet previous decisions, so will defer to folks who were part of those earlier discussions.

@jacobshandling
jacobshandling force-pushed the write-new-upgrade-code-to-existing-titles-without-one branch from 83f6738 to f65ae20 Compare November 25, 2025 03:06
@jacobshandling

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Nov 25, 2025

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@getvictor getvictor self-assigned this Nov 25, 2025
for _, title := range existingSoftwareTitlesForNewSoftwareWithoutBundleIdentifier {
checksums, ok := uniqueTitleStrToChecksums[UniqueSoftwareTitleStr(title.Name, title.Source, title.ExtensionFor)]
for _, titleSummary := range existingTitleSummariesForNewSoftwareWithoutBundleIdentifier {
// TODO - need to factor upgrade_code in here?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

No, because you will update the upgrade code, right?

Do you have a test where software comes in with the same name/source/extensionFor but different upgrade codes? What is supposed to happen? We only save one of the upgrade codes?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

All of the tests in software_upgrade_code_test.go handle the situation where current title's
source and name and incoming software's source and name (and extension for, since not specified) are
the same, but upgrade_code differs

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I do recommend adding a test for 2 software with the same (name, source, extension_for) but different upgrade_code values

Is that what you're referring to, or a different case?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Yes. Sounds good.

}

var retrievedTitleSummaries []fleet.SoftwareTitleSummary
// TODO - include UpgradeCode in the below WHERE (these args) for additional specificity?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Same as previous TODO comment. We overwrite the upgrade code if 2 titles have the same everything else, right? Which means this WHERE should only return the 1 title?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

UPDATE: I understand we don't overwrite, but both software items should get associated to the same title in the DB.

// For others, match by name
nameMatches := td.Name == title.Name
nameMatches := titleSummary.Name == title.Name
// TODO - similarly match if UpgradeCodes match?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Only if we allow same software names to have different upgrade codes. Which I don't think we intend to right now, right?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Right

Comment thread server/fleet/software.go

// SoftwareTitleSummary contains a lightweight subset of the fields of a SoftwareTitle that are
// useful for processing incoming software
// TODO - embed this in `SoftwareTitle` to reduce redundancy

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Nit. I recommend against leaving TODOs in code. If you really want to remember this, file an issue and put the link to the issue here without the TODO.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Will keep this in mind going forward, thanks!

@getvictor getvictor left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks for digging into this, Jacob.
I don't see any issues with this PR.

But I do recommend adding a test for 2 software with the same (name, source, extension_for) but different upgrade_code values, to make sure nothing is completely broken. This can be done in a separate PR.

@jacobshandling
jacobshandling merged commit 27e0de9 into main Nov 25, 2025
40 checks passed
@jacobshandling
jacobshandling deleted the write-new-upgrade-code-to-existing-titles-without-one branch November 25, 2025 22:14
jacobshandling added a commit that referenced this pull request Nov 25, 2025
…sting correlated `software_title`s (#36175)

<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #35724 

This PR addresses Issue 3 in the above unreleased bug: when a new
`software` comes in, its `upgrade_code` is compared with that of any
corresponding `software_title`s, and reconciled appropriately - see code
for the various cases

## Testing

- [x] Added/updated automated tests
- [x] QA'd all new/changed functionality manually
- [x] Load tests: @AndreyKizimenko since I don't think you've load
tested the original story yet, this change will be covered by those
tests when you do

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Bug Fixes**
* Ensured upgrade_code values on existing software titles are
consistently updated when incoming data provides non-empty values; added
logging and safe retry handling for anomalous states.

* **Tests**
* Added table-driven tests covering upgrade_code reconciliation
scenarios (empty, non-empty, conflicting, NULL) and verification of
resulting title values.

* **Refactor**
* Introduced a lightweight software title summary type and updated
internal mappings to streamline pre-insert and reconciliation
processing.

<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
@coderabbitai coderabbitai Bot mentioned this pull request Jan 2, 2026
3 tasks
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.

Upgrade code is inconsistently displayed for Windows software

3 participants