The Inbox: MonticelloConfigurations-ct.166.mcz

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
7 messages Options
Reply | Threaded
Open this post in threaded view
|

The Inbox: MonticelloConfigurations-ct.166.mcz

commits-2
A new version of MonticelloConfigurations was added to project The Inbox:
http://source.squeak.org/inbox/MonticelloConfigurations-ct.166.mcz

==================== Summary ====================

Name: MonticelloConfigurations-ct.166
Author: ct
Time: 8 April 2021, 10:30:30.702818 pm
UUID: 6d887a86-fe0f-8e4c-a878-e40e8e612918
Ancestors: MonticelloConfigurations-mt.165

Adds support in MCMcmUpdater to install updates up to a specified system version number. This can be helpful when you want to bring an image into an exact state of an alpha version. I.e., a bug report or a tool can refer to Squeak6.0 Alpha #19570. Now it's possible to bring your 5.3 image into this state by evaluating:

        MCMcmUpdater default doUpdate: true upToUpdate: 19570.

Still, it is possible to install all updates or navigate forward to a certain upstream version using the existing protocol:

        MCMcmUpdater default doUpdate: true.
        MCMcmUpdater default doUpdate: true upTo: 470.

Detailed changelog (anticlimax):

- Added 'upToUpdate:' variant of update selectors on MCMcmUpdater.
- Refactored existing update selectors to deduplicate the logic for doing a complete update (#doUpdate: etc.) versus installing all updates up to an upstream version (#doUpdate:upTo: etc.).
- Added #currentSystemVersion and #totalSystemVersion on MCConfiguration to determine the current resp. eventual system version as implied by the update stream.
- Renamed (+ deprecated) #depsSatisfying:version(s)Do:displayingProgress: due to misleading use of singular in the block keyword.
- Improved multilingual support for the updater.
- Improved documentation of the update protocol.

Note that in some situations, a small number of follow-up updates will be installed that go beyond the specified system version number. This is caused by interdependencies between the target version and its postdecessors when a definiton was moved from one package into another one. See sender of MCReorganizationPreloader.

=============== Diff against MonticelloConfigurations-mt.165 ===============

Item was added:
+ ----- Method: MCConfiguration class>>ensureOpenTranscript:during: (in category 'preferences') -----
+ ensureOpenTranscript: aBoolean during: aBlock
+
+ | previous |
+ previous := self ensureOpenTranscript.
+ self ensureOpenTranscript: aBoolean.
+ ^ aBlock ensure: [
+ self ensureOpenTranscript: previous]!

Item was added:
+ ----- Method: MCConfiguration>>currentSystemVersion (in category 'updating') -----
+ currentSystemVersion
+ "Answer the current system version found in my configuration (or the associated working copy)."
+
+ ^ self systemVersionForDeps: (self dependencies
+ collect: [:dep |
+ dep isFulfilledByAncestors
+ ifTrue: [dep versionInfo]
+ ifFalse: [dep package workingCopy ancestors] ] )!

Item was added:
+ ----- Method: MCConfiguration>>depsSatisfying:upToUpdate:versionsDo:displayingProgress: (in category 'private') -----
+ depsSatisfying: selectBlock upToUpdate: anIntegerOrNil versionsDo: versionBlock displayingProgress: progressString
+
+ | count selectedVersions cleanWorkingCopies |
+ self cacheAllFileNamesDuring: [
+ self repositories do: [ :eachRepository |
+ MCRepositoryGroup default addRepository: eachRepository ].
+
+ "First, download selected versions"
+ count := 0.
+ selectedVersions := OrderedCollection new.
+ self withProgress: progressString in: self dependencies do: [ :dep | | verName repo |
+ verName := dep versionInfo name.
+ self class extraProgressInfo ifTrue:
+ [ ProgressNotification signal: '' extra: ('Downloading {1}' translated format: {verName}) ].
+ repo := self repositories
+ detect: [ :eachRepository | eachRepository includesVersionNamed: verName ]
+ ifNone: [ self logError: ('Version {1} not found in any repository' translated format: {verName}).
+ self logError: 'Aborting' translated.
+ ^ count ].
+ (selectBlock value: dep) ifTrue: [ | version |
+ version := self versionNamed: verName for: dep from: repo.
+ version ifNil: [ self logError: ('Could not download version {1} from {2}' translated format: {verName. repo description}).
+ self logError: 'Aborting' translated.
+ ^ count ].
+ dep package workingCopy newRepositoryGroupIfDefault. "fix old working copies"
+ dep package workingCopy repositoryGroup addRepository: repo.
+ selectedVersions add: version ] ].
+
+ "Then, process only those definitions that moved from one package to another, to avoid order dependence"
+ cleanWorkingCopies := MCWorkingCopy allManagers select:
+ [ :wc | wc modified not and:
+ [ selectedVersions anySatisfy: [ :v | wc package = v package ] ] ].
+ MCReorganizationPreloader preloadMovesBetween: selectedVersions.
+
+ "Finally, load/merge selected versions"
+ [:exit | self withProgress: progressString in: selectedVersions do: [ :version |
+ self logUpdate: version package with: version.
+ self class extraProgressInfo ifTrue:
+ [ ProgressNotification signal: '' extra: ('Installing {1}' translated format: {version info name}) ].
+ versionBlock value: version.
+ count := count + 1.
+ anIntegerOrNil ifNotNil: [
+ self currentSystemVersion highestUpdate < anIntegerOrNil ifFalse: exit.
+ self flag: #optimization. "ct: By customizing the system version computation, we could save redundant version downloads above. However, because of the preloading stage, this might become a bit more sophisticated." ] ].
+ ] valueWithExit.
+
+ "Clean up packages made dirty by MCReorganizationPreloader"
+ cleanWorkingCopies
+ select: [ :wc | wc modified ]
+ thenDo: [ :wc | wc checkModified ].
+ ].
+ ^ count!

Item was changed:
  ----- Method: MCConfiguration>>depsSatisfying:versionDo:displayingProgress: (in category 'private') -----
+ depsSatisfying: selectBlock versionDo: versionBlock displayingProgress: progressString
+
+ self deprecated.
+ ^ self
+ depsSatisfying: selectBlock
+ versionsDo: versionBlock
+ displayingProgress: progressString!
- depsSatisfying: selectBlock versionDo: verBlock displayingProgress: progressString
- | count selectedVersions cleanWorkingCopies |
- self cacheAllFileNamesDuring: [
- self repositories do: [ :eachRepository |
- MCRepositoryGroup default addRepository: eachRepository ].
- "First, download selected versions"
- count := 0.
- selectedVersions := OrderedCollection new.
- self withProgress: progressString in: self dependencies do: [ :dep | | verName repo |
- verName := dep versionInfo name.
- self class extraProgressInfo ifTrue:
- [ ProgressNotification signal: '' extra: 'Downloading ' , verName ].
- repo := self repositories
- detect: [ :eachRepository | eachRepository includesVersionNamed: verName ]
- ifNone: [ self logError: 'Version ' , verName , ' not found in any repository'.
- self logError: 'Aborting'.
- ^ count ].
- (selectBlock value: dep) ifTrue: [ | version |
- version := self versionNamed: verName for: dep from: repo.
- version ifNil: [ self logError: 'Could not download version ' , verName , ' from ' , repo description.
- self logError: 'Aborting'.
- ^ count ].
- dep package workingCopy newRepositoryGroupIfDefault. "fix old working copies"
- dep package workingCopy repositoryGroup addRepository: repo.
- selectedVersions add: version]].
- "Then, process only those definitions that moved from one package to another, to avoid order dependence"
- cleanWorkingCopies := MCWorkingCopy allManagers select:
- [ :wc | wc modified not and:
- [ selectedVersions anySatisfy: [ :v | wc package = v package ] ] ].
- MCReorganizationPreloader preloadMovesBetween: selectedVersions.
- "Finally, load/merge selected versions"
- self withProgress: progressString in: selectedVersions do: [ :version |
- self logUpdate: version package with: version.
- self class extraProgressInfo ifTrue:
- [ ProgressNotification signal: '' extra: 'Installing ' , version info name ].
- verBlock value: version.
- count := count + 1 ].
- "Clean up packages made dirty by MCReorganizationPreloader"
- cleanWorkingCopies
- select: [ :wc | wc modified ]
- thenDo: [ :wc | wc checkModified ].
- ].
- ^ count!

Item was added:
+ ----- Method: MCConfiguration>>depsSatisfying:versionsDo:displayingProgress: (in category 'private') -----
+ depsSatisfying: selectBlock versionsDo: versionBlock displayingProgress: progressString
+
+ ^ self
+ depsSatisfying: selectBlock
+ upToUpdate: nil
+ versionsDo: versionBlock
+ displayingProgress: progressString!

Item was changed:
  ----- Method: MCConfiguration>>load (in category 'actions') -----
  load
  ^self depsSatisfying: [:dep | dep isCurrent not]
+ versionsDo: [:ver | ver load]
+ displayingProgress: 'loading packages' translated!
- versionDo: [:ver | ver load]
- displayingProgress: 'loading packages'
- !

Item was changed:
  ----- Method: MCConfiguration>>merge (in category 'actions') -----
  merge
  ^self depsSatisfying: [:dep | dep isFulfilledByAncestors not]
+ versionsDo: [:ver | ver merge]
+ displayingProgress: 'merging packages' translated!
- versionDo: [:ver | ver merge]
- displayingProgress: 'merging packages'
- !

Item was changed:
  ----- Method: MCConfiguration>>setSystemVersion (in category 'updating') -----
  setSystemVersion
- "Set the current system version date to the latest date found in my configuration (or the associated working copy). Also set the highest update number to the sum of version numbers in my configuration."
 
+ | newVersion |
+ newVersion := self currentSystemVersion.
- | versionNumbers versionDates |
- versionNumbers := self dependencies collect: [:d |
- (d versionInfo name copyAfterLast: $.) asInteger].
- versionDates := self dependencies collect: [:d |
- d versionInfo date
- ifNil: [d package workingCopy ancestors first date]].
  SystemVersion current
+ date: newVersion date;
+ highestUpdate: newVersion highestUpdate.!
- date: versionDates max;
- highestUpdate: versionNumbers sum.!

Item was added:
+ ----- Method: MCConfiguration>>systemVersionForDeps: (in category 'updating') -----
+ systemVersionForDeps: dependencies
+ "Answer the system version that will be reached by loading the passed dependencies."
+
+ | versionNumbers versionDates |
+ versionNumbers := dependencies collect: [:dep |
+ dep isCollection
+ ifFalse: [dep name versionNumber]
+ ifTrue: [(dep collect: [:version |
+ version name versionNumber])
+ ifEmpty: [0]
+ ifNotEmpty: #max]].
+ versionDates := dependencies with: self dependencies collect: [:dep :originalDep |
+ (dep isCollection ifFalse: [dep date])
+ ifNil: [originalDep package workingCopy ancestors
+ ifEmpty: [nil]
+ ifNotEmpty: [:ancestors | ancestors first date]]].
+
+ ^ SystemVersion new
+ date: ((versionDates copyWithout: nil)
+ ifEmpty: [nil]
+ ifNotEmpty: #max);
+ highestUpdate: versionNumbers sum!

Item was added:
+ ----- Method: MCConfiguration>>totalSystemVersion (in category 'updating') -----
+ totalSystemVersion
+ "Answer the total system version that will be reached by upgrading the receiver."
+
+ ^ self systemVersionForDeps: (self dependencies collect: #versionInfo)!

Item was changed:
  ----- Method: MCConfiguration>>upgrade (in category 'actions') -----
  upgrade
+
+ ^ self upgradeUpToUpdate: nil!
- ^self depsSatisfying:
- [:dep | dep isFulfilledByAncestors not]
- versionDo:
- [:ver |
- (self class upgradeIsMerge and: [ver shouldMerge])
- ifFalse: [ver load]
- ifTrue:
- [[ver merge]
- on: MCNoChangesException
- do: [:req| req resume ]
- on: MCMergeResolutionRequest
- do: [:request |
- request merger conflicts isEmpty
- ifTrue: [request resume: true]
- ifFalse: [request pass]]
- on: Deprecation
- do: [:req| req resume ]]]
- displayingProgress: 'upgrading packages'!

Item was added:
+ ----- Method: MCConfiguration>>upgradeUpToUpdate: (in category 'actions') -----
+ upgradeUpToUpdate: anIntegerOrNil
+
+ ^ self
+ depsSatisfying: [:dep |
+ dep isFulfilledByAncestors not]
+ upToUpdate: anIntegerOrNil
+ versionsDo: [:ver |
+ (self class upgradeIsMerge and: [ver shouldMerge])
+ ifFalse: [ver load]
+ ifTrue:
+ [[ver merge]
+ on: MCNoChangesException
+ do: [:req | req resume ]
+ on: MCMergeResolutionRequest
+ do: [:request |
+ request merger conflicts isEmpty
+ ifTrue: [request resume: true]
+ ifFalse: [request pass]]
+ on: Deprecation
+ do: [:req | req resume ]]]
+ displayingProgress: 'upgrading packages' translated!

Item was added:
+ ----- Method: MCMcmUpdater>>basicDoUpdate:do: (in category 'updating') -----
+ basicDoUpdate: interactive do: aBlock
+ "Update the image by evaluating aBlock. If this is the default updater for the system, update the system version when complete. If interactive, use a modal notifier, otherwise only update the transcript. Flush all caches. If a previous download failed, this is often helpful."
+
+ | config previousUpdateLevel |
+ previousUpdateLevel := SystemVersion current highestUpdate.
+ MCFileBasedRepository flushAllCaches.
+
+ MCConfiguration ensureOpenTranscript: interactive during: [
+ config := aBlock value.
+
+ config ifNil: [
+ interactive ifTrue: [^ self inform: 'Unable to retrieve updates from remote repository.' translated].
+ Transcript cr; show: '==========  Unable to retrieve updates from remote repository. ==========' translated; cr.
+ ^ self ].
+
+ MCMcmUpdater default == self
+ ifTrue: [
+ config setSystemVersion.
+ interactive ifTrue: [
+ self inform: (self updateMessageFor: previousUpdateLevel)].
+ Transcript cr;
+ show: '==========  Update completed:  ' translated;
+ show: previousUpdateLevel;
+ show: ' -> ' ;
+ show: SystemVersion current highestUpdate;
+ show: ' =========='; cr ]
+ ifFalse: [
+ interactive
+ ifTrue: [ self inform: 'Update completed.' ].
+ Transcript cr; show: '==========  Update completed. ==========' translated; cr ] ].!

Item was added:
+ ----- Method: MCMcmUpdater>>basicUpdateFromRepositories:do: (in category 'updating') -----
+ basicUpdateFromRepositories: repositoryUrls do: repositoryBlock
+
+ | repos config |
+ MCConfiguration upgradeIsMerge: true.
+ "The list of repositories to consult in order"
+ repos := repositoryUrls collect: [:url|  
+ MCRepositoryGroup default repositories
+ detect: [:repo | repo description = url]
+ ifNone: [ | repo |
+ repo := MCHttpRepository location: url.
+ MCRepositoryGroup default addRepository: repo.
+ repo]].
+
+ "The list of updates-author.version.mcm sorted by version"
+ repos do: [:repo |
+ config := repositoryBlock value: repo].
+ ^ config!

Item was added:
+ ----- Method: MCMcmUpdater>>basicUpdateFromRepository:thenDo: (in category 'updating') -----
+ basicUpdateFromRepository: aRepository thenDo: configBlock
+
+ ^ self
+ basicUpdateFromRepository: aRepository
+ whileUpdatesSatisfy: [true]
+ upToUpdate: nil
+ thenDo: configBlock!

Item was added:
+ ----- Method: MCMcmUpdater>>basicUpdateFromRepository:whileUpdatesSatisfy:upToUpdate:thenDo: (in category 'updating') -----
+ basicUpdateFromRepository: aRepository whileUpdatesSatisfy: updateBlock upToUpdate: anIntegerOrNil thenDo: configBlock
+
+ | config |
+ aRepository cacheAllFileNamesDuring: [ | updateList |
+ updateList := self updateListFor: aRepository.
+ "Proceed only if there are updates available at all."
+ updateList ifNotEmpty: [
+ updateList := self refreshUpdateMapFor: aRepository with: updateList.
+ "Now process each update file. Check if we have all dependencies and if not, load the entire configuration (this is mostly to skip older updates quickly)"
+ [:exit | updateList
+ do: [:assoc |
+ (updateBlock cull: assoc key cull: assoc value) ifFalse: exit.
+ ProgressNotification signal: '' extra: ('Processing {1}' translated format: {assoc value}).
+ config := aRepository versionNamed: assoc value.
+ anIntegerOrNil ifNotNil: [
+ config currentSystemVersion highestUpdate <= anIntegerOrNil ifFalse: exit].
+ self updateFromConfig: config upToUpdate: anIntegerOrNil.
+ anIntegerOrNil ifNotNil: [
+ config currentSystemVersion highestUpdate < config totalSystemVersion highestUpdate ifTrue: exit].
+ self lastUpdateMap at: aRepository description put: assoc key]
+ displayingProgress: 'Processing configurations' translated] valueWithExit.
+ configBlock cull: config]].
+ ^ config!

Item was changed:
  ----- Method: MCMcmUpdater>>doUpdate: (in category 'updating') -----
  doUpdate: interactive
+ "Update the image by loading all pending updates from the server."
- "Update the image by loading all pending updates from the server. If this is
- the default updater for the system, update the system version when complete.
- If interteractive use a modal notifier, otherwise only update the transcript.
- Flush all caches. If a previous download failed this is often helpful"
 
+ ^ self basicDoUpdate: interactive do: [
+ self updateFromRepository ]!
- | config previousUpdateLevel ensureTranscriptSetting |
- previousUpdateLevel := SystemVersion current highestUpdate.
- MCFileBasedRepository flushAllCaches.
- ensureTranscriptSetting := MCConfiguration ensureOpenTranscript.
- [ MCConfiguration ensureOpenTranscript: interactive.
- config := self updateFromRepository.
- config ifNil: [
- interactive ifTrue: [ ^self inform: 'Unable to retrieve updates from remote repository.' translated ].
- Transcript cr; show: '==========  Unable to retrieve updates from remote repository. ==========' translated; cr.
- ^ self ].
- MCMcmUpdater default == self
- ifTrue: [
- config setSystemVersion.
- interactive ifTrue: [
- self inform: (self updateMessageFor: previousUpdateLevel)].
- Transcript cr;
- show: '==========  Update completed:  ' translated;
- show: previousUpdateLevel;
- show: ' -> ' ;
- show: SystemVersion current highestUpdate;
- show: ' =========='; cr ]
- ifFalse: [
- interactive
- ifTrue: [ self inform: 'Update completed.' ].
- Transcript cr; show: '==========  Update completed. ==========' translated; cr ] ]
- ensure: [ MCConfiguration ensureOpenTranscript: ensureTranscriptSetting].
-
- !

Item was changed:
  ----- Method: MCMcmUpdater>>doUpdate:upTo: (in category 'updating') -----
  doUpdate: interactive upTo: versionNumber
+ "Update the image by loading all pending updates from the server until update stream with the specified version number has been processed."
- "Update the image by loading all pending updates from the server. If this is
- the default updater for the system, update the system version when complete.
- If interteractive use a modal notifier, otherwise only update the transcript.
- Flush all caches. If a previous download failed this is often helpful"
 
+ ^ self basicDoUpdate: interactive do: [
+ self updateFromRepositories: {self repository} upTo: versionNumber ]!
- | config previousUpdateLevel |
- previousUpdateLevel := SystemVersion current highestUpdate.
- MCFileBasedRepository flushAllCaches.
- config := self updateFromRepositories: { self repository } upTo: versionNumber.
- config ifNil: [
- interactive ifTrue: [ ^self inform: 'Unable to retrieve updates from remote repository.' translated ].
- Transcript cr; show: '==========  Unable to retrieve updates from remote repository. ==========' translated; cr.
- ^ self ].
- MCMcmUpdater default == self
- ifTrue: [
- config setSystemVersion.
- interactive ifTrue: [
- self inform: (self updateMessageFor: previousUpdateLevel)].
- Transcript cr;
- show: '==========  Update completed:  ' translated;
- show: previousUpdateLevel;
- show: ' -> ' ;
- show: SystemVersion current highestUpdate;
- show: ' =========='; cr ]
- ifFalse: [
- interactive
- ifTrue: [ self inform: 'Update completed.' ].
- Transcript cr; show: '==========  Update completed. ==========' translated; cr ]
- !

Item was added:
+ ----- Method: MCMcmUpdater>>doUpdate:upToUpdate: (in category 'updating') -----
+ doUpdate: interactive upToUpdate: anIntegerOrNil
+ "Update the image by loading all pending updates from the server until the system version has reached at least anInteger. If anInteger is nil, install all available updates."
+
+ ^ self basicDoUpdate: interactive do: [
+ self updateFromRepositories: {self repository} upToUpdate: anIntegerOrNil ]!

Item was changed:
  ----- Method: MCMcmUpdater>>doUpdateUpTo: (in category 'updating') -----
  doUpdateUpTo: versionNumber
+ "Update the image by loading all pending updates from the server until update stream with the specified version number has been processed."
- "Update the image by loading all pending updates from the server. If this is
- the default updater for the system, update the system version when complete.
- Flush all caches. If a previous download failed this is often helpful"
 
  ^self doUpdate: true upTo: versionNumber
  !

Item was changed:
  ----- Method: MCMcmUpdater>>updateFromConfig: (in category 'updating') -----
  updateFromConfig: config
 
+ ^ self updateFromConfig: config upToUpdate: nil!
- "Skip packages that were specifically unloaded"
- config dependencies: (config dependencies
- reject: [:dep| self class skipPackages includes: dep package name]).
- self class updateMissingPackages ifFalse:[
- "Skip packages that are not in the image"
- config dependencies: (config dependencies
- select: [:dep| dep package hasWorkingCopy])].
- (config dependencies allSatisfy:[:dep| dep isFulfilled])
- ifFalse:[config upgrade].
- !

Item was added:
+ ----- Method: MCMcmUpdater>>updateFromConfig:upToUpdate: (in category 'updating') -----
+ updateFromConfig: config upToUpdate: anIntegerOrNil
+
+ "Skip packages that were specifically unloaded"
+ config dependencies: (config dependencies
+ reject: [:dep | self class skipPackages includes: dep package name]).
+ self class updateMissingPackages ifFalse: [
+ "Skip packages that are not in the image"
+ config dependencies: (config dependencies
+ select: [:dep | dep package hasWorkingCopy])].
+ (config dependencies allSatisfy: [:dep | dep isFulfilled])
+ ifFalse: [config upgradeUpToUpdate: anIntegerOrNil].!

Item was changed:
  ----- Method: MCMcmUpdater>>updateFromRepositories:upTo: (in category 'updating') -----
  updateFromRepositories: repositoryUrls upTo: versionNumber
+ "
+ MCMcmUpdater default updateFromRepositories: #('http://squeaksource.com/MCUpdateTest') upTo: 3
+ "
- "MCMcmUpdater updateFromRepositories: #(
- 'http://squeaksource.com/MCUpdateTest'
- )"
 
+ ^ self basicUpdateFromRepositories: repositoryUrls do: [:repo |
+ self updateFromRepository: repo upTo: versionNumber]!
- | repos config |
- MCConfiguration upgradeIsMerge: true.
- "The list of repositories to consult in order"
- repos := repositoryUrls collect:[:url|
- MCRepositoryGroup default repositories
- detect:[:r| r description = url]
- ifNone:[ | r |
- r := MCHttpRepository location: url user: '' password: ''.
- MCRepositoryGroup default addRepository: r.
- r]].
-
- "The list of updates-author.version.mcm sorted by version"
- repos do:[ :r | config := self updateFromRepository: r upTo: versionNumber].
- ^config!

Item was added:
+ ----- Method: MCMcmUpdater>>updateFromRepositories:upToUpdate: (in category 'updating') -----
+ updateFromRepositories: repositoryUrls upToUpdate: anIntegerOrNil
+ "
+ MCMcmUpdater default updateFromRepositories: #('http://squeaksource.com/MCUpdateTest') upToUpdate: 2130
+ "
+
+ ^ self basicUpdateFromRepositories: repositoryUrls do: [:repo |
+ self updateFromRepository: repo upToUpdate: anIntegerOrNil ]!

Item was changed:
  ----- Method: MCMcmUpdater>>updateFromRepository (in category 'updating') -----
  updateFromRepository
 
+ ^ self updateFromRepository: self getRepositoryFromRepositoryGroup!
- | config repo |
- repo := self getRepositoryFromRepositoryGroup.
- repo cacheAllFileNamesDuring: [ | updateList |
- updateList := self updateListFor: repo.
- "Proceed only if there are updates available at all."
- updateList ifNotEmpty: [
- updateList := self refreshUpdateMapFor: repo with: updateList.
- "Now process each update file. Check if we have all dependencies and if not,
- load the entire configuration (this is mostly to skip older updates quickly)"
- updateList do:[:assoc|
- ProgressNotification signal: '' extra: 'Processing ', assoc value.
- config := repo versionNamed: assoc value.
- self updateFromConfig: config.
- self lastUpdateMap at: repo description put: assoc key.
- ] displayingProgress: 'Processing configurations'.
- "We've loaded all the provided update configurations.
- Use the latest configuration to update all the remaining packages."
- (self useLatestPackagesFrom: repo) ifTrue: [
- config updateFromRepositories.
- config upgrade].
- ]].
- ^ config
- !

Item was added:
+ ----- Method: MCMcmUpdater>>updateFromRepository: (in category 'updating') -----
+ updateFromRepository: aRepository
+
+ ^ self basicUpdateFromRepository: aRepository thenDo: [:config |
+ "We've loaded all the provided update configurations. Use the latest configuration to update all the remaining packages."
+ (self useLatestPackagesFrom: aRepository) ifTrue: [
+ config updateFromRepositories.
+ config upgrade]]!

Item was changed:
  ----- Method: MCMcmUpdater>>updateFromRepository:upTo: (in category 'updating') -----
+ updateFromRepository: aRepository upTo: versionNumber
- updateFromRepository: repository upTo: versionNumber
 
+ ^ self
+ basicUpdateFromRepository: aRepository
+ whileUpdatesSatisfy: [:updateVersion | updateVersion <= versionNumber]
+ upToUpdate: nil
+ thenDo: []!
- | config |
- repository cacheAllFileNamesDuring: [ | updateList |
- updateList := self updateListFor: repository.
- "Proceed only if there are updates available at all."
- updateList ifNotEmpty: [
- updateList := self refreshUpdateMapFor: repository with: updateList.
- "Now process each update file. Check if we have all dependencies and if not,
- load the entire configuration (this is mostly to skip older updates quickly)"
- updateList do:[:assoc|
- assoc key > versionNumber ifTrue: [^config].
- ProgressNotification signal: '' extra: 'Processing ', assoc value.
- config := repository versionNamed: assoc value.
- self updateFromConfig: config.
- self lastUpdateMap at: repository description put: assoc key.
- ] displayingProgress: 'Processing configurations'.
- ]].
- ^config
- !

Item was added:
+ ----- Method: MCMcmUpdater>>updateFromRepository:upToUpdate: (in category 'updating') -----
+ updateFromRepository: aRepository upToUpdate: anIntegerOrNil
+
+ ^ self
+ basicUpdateFromRepository: aRepository
+ whileUpdatesSatisfy: [true]
+ upToUpdate: anIntegerOrNil
+ thenDo: []!


Reply | Threaded
Open this post in threaded view
|

Re: The Inbox: MonticelloConfigurations-ct.166.mcz

Christoph Thiede

Please review! :-)


A concrete use case I was having in mind for this feature is to allow arbitrary Squeak Trunk version numbers in smalltalkCI, see: https://github.com/hpi-swa/smalltalkCI/pull/506

At the moment, it is WIP - an unpolished prototype only, but the idea is that you could specify something like Squeak64-18242 instead of Squeak64-trunk in the CI/CLI args.


Nevertheless, even if the proposal for SCI should get rejected, I think this is a helpful feature because releases are quite rare in Squeak and I find it important to tag certain states in the Trunk evolution.

To improve support for the SCI PR, I would also like to backport these changes to 5.3 - there was only a single merge conflict, so if you agree with this proposal, I can send you a changeset for 5.3. Thanks in advance! :-)

Best,
Christoph

Von: Squeak-dev <[hidden email]> im Auftrag von [hidden email] <[hidden email]>
Gesendet: Donnerstag, 8. April 2021 22:30:32
An: [hidden email]
Betreff: [squeak-dev] The Inbox: MonticelloConfigurations-ct.166.mcz
 
A new version of MonticelloConfigurations was added to project The Inbox:
http://source.squeak.org/inbox/MonticelloConfigurations-ct.166.mcz

==================== Summary ====================

Name: MonticelloConfigurations-ct.166
Author: ct
Time: 8 April 2021, 10:30:30.702818 pm
UUID: 6d887a86-fe0f-8e4c-a878-e40e8e612918
Ancestors: MonticelloConfigurations-mt.165

Adds support in MCMcmUpdater to install updates up to a specified system version number. This can be helpful when you want to bring an image into an exact state of an alpha version. I.e., a bug report or a tool can refer to Squeak6.0 Alpha #19570. Now it's possible to bring your 5.3 image into this state by evaluating:

        MCMcmUpdater default doUpdate: true upToUpdate: 19570.

Still, it is possible to install all updates or navigate forward to a certain upstream version using the existing protocol:

        MCMcmUpdater default doUpdate: true.
        MCMcmUpdater default doUpdate: true upTo: 470.

Detailed changelog (anticlimax):

- Added 'upToUpdate:' variant of update selectors on MCMcmUpdater.
- Refactored existing update selectors to deduplicate the logic for doing a complete update (#doUpdate: etc.) versus installing all updates up to an upstream version (#doUpdate:upTo: etc.).
- Added #currentSystemVersion and #totalSystemVersion on MCConfiguration to determine the current resp. eventual system version as implied by the update stream.
- Renamed (+ deprecated) #depsSatisfying:version(s)Do:displayingProgress: due to misleading use of singular in the block keyword.
- Improved multilingual support for the updater.
- Improved documentation of the update protocol.

Note that in some situations, a small number of follow-up updates will be installed that go beyond the specified system version number. This is caused by interdependencies between the target version and its postdecessors when a definiton was moved from one package into another one. See sender of MCReorganizationPreloader.

=============== Diff against MonticelloConfigurations-mt.165 ===============

Item was added:
+ ----- Method: MCConfiguration class>>ensureOpenTranscript:during: (in category 'preferences') -----
+ ensureOpenTranscript: aBoolean during: aBlock
+
+        | previous |
+        previous := self ensureOpenTranscript.
+        self ensureOpenTranscript: aBoolean.
+        ^ aBlock ensure: [
+                self ensureOpenTranscript: previous]!

Item was added:
+ ----- Method: MCConfiguration>>currentSystemVersion (in category 'updating') -----
+ currentSystemVersion
+        "Answer the current system version found in my configuration (or the associated working copy)."
+
+        ^ self systemVersionForDeps: (self dependencies
+                collect: [:dep |
+                        dep isFulfilledByAncestors
+                                ifTrue: [dep versionInfo]
+                                ifFalse: [dep package workingCopy ancestors] ] )!

Item was added:
+ ----- Method: MCConfiguration>>depsSatisfying:upToUpdate:versionsDo:displayingProgress: (in category 'private') -----
+ depsSatisfying: selectBlock upToUpdate: anIntegerOrNil versionsDo: versionBlock displayingProgress: progressString
+
+        | count selectedVersions cleanWorkingCopies |
+        self cacheAllFileNamesDuring: [
+                self repositories do: [ :eachRepository |
+                        MCRepositoryGroup default addRepository: eachRepository ].
+               
+                "First, download selected versions"
+                count := 0.
+                selectedVersions := OrderedCollection new.
+                self withProgress: progressString in: self dependencies do: [ :dep | | verName repo |
+                        verName := dep versionInfo name.
+                        self class extraProgressInfo ifTrue:
+                                [ ProgressNotification signal: '' extra: ('Downloading {1}' translated format: {verName}) ].
+                        repo := self repositories
+                                detect: [ :eachRepository | eachRepository includesVersionNamed: verName ]
+                                ifNone:  [ self logError: ('Version {1} not found in any repository' translated format: {verName}).
+                                        self logError: 'Aborting' translated.
+                                        ^ count ].
+                        (selectBlock value: dep) ifTrue: [ | version |
+                                version := self versionNamed: verName for: dep from: repo.
+                                version ifNil: [ self logError: ('Could not download version {1} from {2}' translated format: {verName. repo description}).
+                                        self logError: 'Aborting' translated.
+                                        ^ count ].
+                                dep package workingCopy newRepositoryGroupIfDefault. "fix old working copies"
+                                dep package workingCopy repositoryGroup addRepository: repo.
+                                selectedVersions add: version ] ].
+               
+                "Then, process only those definitions that moved from one package to another, to avoid order dependence"
+                cleanWorkingCopies := MCWorkingCopy allManagers select:
+                        [ :wc | wc modified not and:
+                                [ selectedVersions anySatisfy: [ :v | wc package = v package ] ] ].
+                MCReorganizationPreloader preloadMovesBetween: selectedVersions.
+               
+                "Finally, load/merge selected versions"
+                [:exit | self withProgress: progressString in: selectedVersions do: [ :version |
+                        self logUpdate: version package with: version.
+                        self class extraProgressInfo ifTrue:
+                                [ ProgressNotification signal: '' extra: ('Installing {1}' translated format: {version info name}) ].
+                        versionBlock value: version.
+                        count := count + 1.
+                        anIntegerOrNil ifNotNil: [
+                                self currentSystemVersion highestUpdate < anIntegerOrNil ifFalse: exit.
+                                self flag: #optimization. "ct: By customizing the system version computation, we could save redundant version downloads above. However, because of the preloading stage, this might become a bit more sophisticated." ] ].
+                ] valueWithExit.
+               
+                "Clean up packages made dirty by MCReorganizationPreloader"
+                cleanWorkingCopies
+                        select: [ :wc | wc modified ]
+                        thenDo: [ :wc | wc checkModified ].
+        ].
+        ^ count!

Item was changed:
  ----- Method: MCConfiguration>>depsSatisfying:versionDo:displayingProgress: (in category 'private') -----
+ depsSatisfying: selectBlock versionDo: versionBlock displayingProgress: progressString
+
+        self deprecated.
+        ^ self
+                depsSatisfying: selectBlock
+                versionsDo: versionBlock
+                displayingProgress: progressString!
- depsSatisfying: selectBlock versionDo: verBlock displayingProgress: progressString
-        | count selectedVersions cleanWorkingCopies |
-        self cacheAllFileNamesDuring: [
-                self repositories do: [ :eachRepository |
-                        MCRepositoryGroup default addRepository: eachRepository ].
-                "First, download selected versions"
-                count := 0.
-                selectedVersions := OrderedCollection new.
-                self withProgress: progressString in: self dependencies do: [ :dep | | verName repo |
-                        verName := dep versionInfo name.
-                        self class extraProgressInfo ifTrue:
-                                [ ProgressNotification signal: '' extra: 'Downloading ' , verName ].
-                        repo := self repositories
-                                detect: [ :eachRepository | eachRepository includesVersionNamed: verName ]
-                                ifNone:  [ self logError: 'Version ' , verName , ' not found in any repository'.
-                                        self logError: 'Aborting'.
-                                        ^ count ].
-                        (selectBlock value: dep) ifTrue: [ | version |
-                                version := self versionNamed: verName for: dep from: repo.
-                                version ifNil: [ self logError: 'Could not download version ' , verName , ' from ' , repo description.
-                                        self logError: 'Aborting'.
-                                        ^ count ].
-                                dep package workingCopy newRepositoryGroupIfDefault. "fix old working copies"
-                                dep package workingCopy repositoryGroup addRepository: repo.
-                                selectedVersions add: version]].
-                "Then, process only those definitions that moved from one package to another, to avoid order dependence"
-                cleanWorkingCopies := MCWorkingCopy allManagers select:
-                        [ :wc | wc modified not and:
-                                [ selectedVersions anySatisfy: [ :v | wc package = v package ] ] ].
-                MCReorganizationPreloader preloadMovesBetween: selectedVersions.
-                "Finally, load/merge selected versions"
-                self withProgress: progressString in: selectedVersions do: [ :version |
-                        self logUpdate: version package with: version.
-                        self class extraProgressInfo ifTrue:
-                                [ ProgressNotification signal: '' extra: 'Installing ' , version info name ].
-                        verBlock value: version.
-                        count := count + 1 ].
-                "Clean up packages made dirty by MCReorganizationPreloader"
-                cleanWorkingCopies
-                        select: [ :wc | wc modified ]
-                        thenDo: [ :wc | wc checkModified ].
-        ].
-        ^ count!

Item was added:
+ ----- Method: MCConfiguration>>depsSatisfying:versionsDo:displayingProgress: (in category 'private') -----
+ depsSatisfying: selectBlock versionsDo: versionBlock displayingProgress: progressString
+
+        ^ self
+                depsSatisfying: selectBlock
+                upToUpdate: nil
+                versionsDo: versionBlock
+                displayingProgress: progressString!

Item was changed:
  ----- Method: MCConfiguration>>load (in category 'actions') -----
  load
         ^self depsSatisfying: [:dep | dep isCurrent not]
+                versionsDo: [:ver | ver load]
+                displayingProgress: 'loading packages' translated!
-                versionDo: [:ver | ver load]
-                displayingProgress: 'loading packages'
- !

Item was changed:
  ----- Method: MCConfiguration>>merge (in category 'actions') -----
  merge
         ^self depsSatisfying: [:dep | dep isFulfilledByAncestors not]
+                versionsDo: [:ver | ver merge]
+                displayingProgress: 'merging packages' translated!
-                versionDo: [:ver | ver merge]
-                displayingProgress: 'merging packages'
- !

Item was changed:
  ----- Method: MCConfiguration>>setSystemVersion (in category 'updating') -----
  setSystemVersion
-        "Set the current system version date to the latest date found in my configuration (or the associated working copy). Also set the highest update number to the sum of version numbers in my configuration."
 
+        | newVersion |
+        newVersion := self currentSystemVersion.
-        | versionNumbers versionDates |
-        versionNumbers := self dependencies collect: [:d |
-                (d versionInfo name copyAfterLast: $.) asInteger].
-        versionDates := self dependencies collect: [:d |
-                d versionInfo date
-                        ifNil: [d package workingCopy ancestors first date]].
         SystemVersion current
+                date: newVersion date;
+                highestUpdate: newVersion highestUpdate.!
-                date: versionDates max;
-                highestUpdate: versionNumbers sum.!

Item was added:
+ ----- Method: MCConfiguration>>systemVersionForDeps: (in category 'updating') -----
+ systemVersionForDeps: dependencies
+        "Answer the system version that will be reached by loading the passed dependencies."
+
+        | versionNumbers versionDates |
+        versionNumbers := dependencies collect: [:dep |
+                dep isCollection
+                        ifFalse: [dep name versionNumber]
+                        ifTrue: [(dep collect: [:version |
+                                version name versionNumber])
+                                        ifEmpty: [0]
+                                        ifNotEmpty: #max]].
+        versionDates := dependencies with: self dependencies collect: [:dep :originalDep |
+                (dep isCollection ifFalse: [dep date])
+                        ifNil: [originalDep package workingCopy ancestors
+                                        ifEmpty: [nil]
+                                        ifNotEmpty: [:ancestors | ancestors first date]]].
+       
+        ^ SystemVersion new
+                date: ((versionDates copyWithout: nil)
+                        ifEmpty: [nil]
+                        ifNotEmpty: #max);
+                highestUpdate: versionNumbers sum!

Item was added:
+ ----- Method: MCConfiguration>>totalSystemVersion (in category 'updating') -----
+ totalSystemVersion
+        "Answer the total system version that will be reached by upgrading the receiver."
+
+        ^ self systemVersionForDeps: (self dependencies collect: #versionInfo)!

Item was changed:
  ----- Method: MCConfiguration>>upgrade (in category 'actions') -----
  upgrade
+
+        ^ self upgradeUpToUpdate: nil!
-        ^self depsSatisfying:
-                        [:dep | dep isFulfilledByAncestors not]
-                versionDo:
-                        [:ver |
-                        (self class upgradeIsMerge and: [ver shouldMerge])
-                                ifFalse: [ver load]
-                                ifTrue:
-                                        [[ver merge]
-                                                on: MCNoChangesException
-                                                do: [:req| req resume ]
-                                                on: MCMergeResolutionRequest
-                                                do: [:request |
-                                                        request merger conflicts isEmpty
-                                                                ifTrue: [request resume: true]
-                                                                ifFalse: [request pass]]
-                                                on: Deprecation
-                                                do: [:req| req resume ]]]
-                displayingProgress: 'upgrading packages'!

Item was added:
+ ----- Method: MCConfiguration>>upgradeUpToUpdate: (in category 'actions') -----
+ upgradeUpToUpdate: anIntegerOrNil
+
+        ^ self
+                depsSatisfying: [:dep |
+                        dep isFulfilledByAncestors not]
+                upToUpdate: anIntegerOrNil
+                versionsDo: [:ver |
+                        (self class upgradeIsMerge and: [ver shouldMerge])
+                                ifFalse: [ver load]
+                                ifTrue:
+                                        [[ver merge]
+                                                on: MCNoChangesException
+                                                do: [:req | req resume ]
+                                                on: MCMergeResolutionRequest
+                                                do: [:request |
+                                                        request merger conflicts isEmpty
+                                                                ifTrue: [request resume: true]
+                                                                ifFalse: [request pass]]
+                                                on: Deprecation
+                                                do: [:req | req resume ]]]
+                displayingProgress: 'upgrading packages' translated!

Item was added:
+ ----- Method: MCMcmUpdater>>basicDoUpdate:do: (in category 'updating') -----
+ basicDoUpdate: interactive do: aBlock
+        "Update the image by evaluating aBlock. If this is the default updater for the system, update the system version when complete. If interactive, use a modal notifier, otherwise only update the transcript. Flush all caches. If a previous download failed, this is often helpful."
+
+        | config previousUpdateLevel |
+        previousUpdateLevel := SystemVersion current highestUpdate.
+        MCFileBasedRepository flushAllCaches.
+       
+        MCConfiguration ensureOpenTranscript: interactive during: [
+                config := aBlock value.
+       
+                config ifNil: [
+                        interactive ifTrue: [^ self inform: 'Unable to retrieve updates from remote repository.' translated].
+                        Transcript cr; show: '==========  Unable to retrieve updates from remote repository. ==========' translated; cr.
+                        ^ self ].
+       
+                MCMcmUpdater default == self
+                        ifTrue: [
+                                config setSystemVersion.
+                                interactive ifTrue: [
+                                        self inform: (self updateMessageFor: previousUpdateLevel)].
+                                Transcript cr;
+                                        show: '==========  Update completed:  ' translated;
+                                        show: previousUpdateLevel;
+                                        show: ' -> ' ;
+                                        show: SystemVersion current highestUpdate;
+                                        show: ' =========='; cr ]
+                        ifFalse: [
+                                interactive
+                                        ifTrue: [ self inform: 'Update completed.' ].
+                                Transcript cr; show: '==========  Update completed. ==========' translated; cr ] ].!

Item was added:
+ ----- Method: MCMcmUpdater>>basicUpdateFromRepositories:do: (in category 'updating') -----
+ basicUpdateFromRepositories: repositoryUrls do: repositoryBlock
+
+        | repos config |
+        MCConfiguration upgradeIsMerge: true.
+        "The list of repositories to consult in order"
+        repos := repositoryUrls collect: [:url| 
+                MCRepositoryGroup default repositories
+                        detect: [:repo | repo description = url]
+                        ifNone: [ | repo |
+                                repo := MCHttpRepository location: url.
+                                MCRepositoryGroup default addRepository: repo.
+                                repo]].
+       
+        "The list of updates-author.version.mcm sorted by version"
+        repos do: [:repo |
+                config := repositoryBlock value: repo].
+        ^ config!

Item was added:
+ ----- Method: MCMcmUpdater>>basicUpdateFromRepository:thenDo: (in category 'updating') -----
+ basicUpdateFromRepository: aRepository thenDo: configBlock
+
+        ^ self
+                basicUpdateFromRepository: aRepository
+                whileUpdatesSatisfy: [true]
+                upToUpdate: nil
+                thenDo: configBlock!

Item was added:
+ ----- Method: MCMcmUpdater>>basicUpdateFromRepository:whileUpdatesSatisfy:upToUpdate:thenDo: (in category 'updating') -----
+ basicUpdateFromRepository: aRepository whileUpdatesSatisfy: updateBlock upToUpdate: anIntegerOrNil thenDo: configBlock
+
+        | config |
+        aRepository cacheAllFileNamesDuring: [ | updateList |
+                updateList := self updateListFor: aRepository.
+                "Proceed only if there are updates available at all."
+                updateList ifNotEmpty: [
+                        updateList := self refreshUpdateMapFor: aRepository with: updateList.
+                        "Now process each update file. Check if we have all dependencies and if not, load the entire configuration (this is mostly to skip older updates quickly)"
+                        [:exit | updateList
+                                do: [:assoc |
+                                        (updateBlock cull: assoc key cull: assoc value) ifFalse: exit.
+                                        ProgressNotification signal: '' extra: ('Processing {1}' translated format: {assoc value}).
+                                        config := aRepository versionNamed: assoc value.
+                                        anIntegerOrNil ifNotNil: [
+                                                config currentSystemVersion highestUpdate <= anIntegerOrNil ifFalse: exit].
+                                        self updateFromConfig: config upToUpdate: anIntegerOrNil.
+                                        anIntegerOrNil ifNotNil: [
+                                                config currentSystemVersion highestUpdate < config totalSystemVersion highestUpdate ifTrue: exit].
+                                        self lastUpdateMap at: aRepository description put: assoc key]
+                                displayingProgress: 'Processing configurations' translated] valueWithExit.
+                        configBlock cull: config]].
+        ^ config!

Item was changed:
  ----- Method: MCMcmUpdater>>doUpdate: (in category 'updating') -----
  doUpdate: interactive
+        "Update the image by loading all pending updates from the server."
-        "Update the image by loading all pending updates from the server. If this is
-        the default updater for the system, update the system version when complete.
-        If interteractive use a modal notifier, otherwise only update the transcript.
-        Flush all caches. If a previous download failed this is often helpful"
 
+        ^ self basicDoUpdate: interactive do: [
+                self updateFromRepository ]!
-        | config previousUpdateLevel ensureTranscriptSetting |
-        previousUpdateLevel := SystemVersion current highestUpdate.
-        MCFileBasedRepository flushAllCaches.  
-        ensureTranscriptSetting := MCConfiguration ensureOpenTranscript.
-        [ MCConfiguration ensureOpenTranscript: interactive.
-        config := self updateFromRepository.
-        config ifNil: [
-                interactive ifTrue: [ ^self inform: 'Unable to retrieve updates from remote repository.' translated ].
-                Transcript cr; show: '==========  Unable to retrieve updates from remote repository. ==========' translated; cr.
-                ^ self ].
-        MCMcmUpdater default == self
-                ifTrue: [
-                        config setSystemVersion.
-                        interactive ifTrue: [
-                                self inform: (self updateMessageFor: previousUpdateLevel)].
-                        Transcript cr;
-                                show: '==========  Update completed:  ' translated;
-                                show: previousUpdateLevel;
-                                show: ' -> ' ;
-                                show: SystemVersion current highestUpdate;
-                                show: ' =========='; cr ]
-                ifFalse: [
-                        interactive
-                                ifTrue: [ self inform: 'Update completed.' ].
-                        Transcript cr; show: '==========  Update completed. ==========' translated; cr ] ]
-                ensure: [ MCConfiguration ensureOpenTranscript: ensureTranscriptSetting].
-
-        !

Item was changed:
  ----- Method: MCMcmUpdater>>doUpdate:upTo: (in category 'updating') -----
  doUpdate: interactive upTo: versionNumber
+        "Update the image by loading all pending updates from the server until update stream with the specified version number has been processed."
-        "Update the image by loading all pending updates from the server. If this is
-        the default updater for the system, update the system version when complete.
-        If interteractive use a modal notifier, otherwise only update the transcript.
-        Flush all caches. If a previous download failed this is often helpful"
 
+        ^ self basicDoUpdate: interactive do: [
+                self updateFromRepositories: {self repository} upTo: versionNumber ]!
-        | config previousUpdateLevel |
-        previousUpdateLevel := SystemVersion current highestUpdate.
-        MCFileBasedRepository flushAllCaches.
-        config := self updateFromRepositories: { self repository } upTo: versionNumber.
-        config ifNil: [
-                interactive ifTrue: [ ^self inform: 'Unable to retrieve updates from remote repository.' translated ].
-                Transcript cr; show: '==========  Unable to retrieve updates from remote repository. ==========' translated; cr.
-                ^ self ].
-        MCMcmUpdater default == self
-                ifTrue: [
-                        config setSystemVersion.
-                        interactive ifTrue: [
-                                self inform: (self updateMessageFor: previousUpdateLevel)].
-                        Transcript cr;
-                                show: '==========  Update completed:  ' translated;
-                                show: previousUpdateLevel;
-                                show: ' -> ' ;
-                                show: SystemVersion current highestUpdate;
-                                show: ' =========='; cr ]
-                ifFalse: [
-                        interactive
-                                ifTrue: [ self inform: 'Update completed.' ].
-                        Transcript cr; show: '==========  Update completed. ==========' translated; cr ]
-        !

Item was added:
+ ----- Method: MCMcmUpdater>>doUpdate:upToUpdate: (in category 'updating') -----
+ doUpdate: interactive upToUpdate: anIntegerOrNil
+        "Update the image by loading all pending updates from the server until the system version has reached at least anInteger. If anInteger is nil, install all available updates."
+
+        ^ self basicDoUpdate: interactive do: [
+                self updateFromRepositories: {self repository} upToUpdate: anIntegerOrNil ]!

Item was changed:
  ----- Method: MCMcmUpdater>>doUpdateUpTo: (in category 'updating') -----
  doUpdateUpTo: versionNumber
+        "Update the image by loading all pending updates from the server until update stream with the specified version number has been processed."
-        "Update the image by loading all pending updates from the server. If this is
-        the default updater for the system, update the system version when complete.
-        Flush all caches. If a previous download failed this is often helpful"
 
         ^self doUpdate: true upTo: versionNumber
  !

Item was changed:
  ----- Method: MCMcmUpdater>>updateFromConfig: (in category 'updating') -----
  updateFromConfig: config
 
+        ^ self updateFromConfig: config upToUpdate: nil!
-        "Skip packages that were specifically unloaded"
-        config dependencies: (config dependencies
-                reject: [:dep| self class skipPackages includes: dep package name]).
-        self class updateMissingPackages ifFalse:[
-                "Skip packages that are not in the image"
-                config dependencies: (config dependencies
-                        select: [:dep| dep package hasWorkingCopy])].
-        (config dependencies allSatisfy:[:dep| dep isFulfilled])
-                ifFalse:[config upgrade].
- !

Item was added:
+ ----- Method: MCMcmUpdater>>updateFromConfig:upToUpdate: (in category 'updating') -----
+ updateFromConfig: config upToUpdate: anIntegerOrNil
+
+        "Skip packages that were specifically unloaded"
+        config dependencies: (config dependencies
+                reject: [:dep | self class skipPackages includes: dep package name]).
+        self class updateMissingPackages ifFalse: [
+                "Skip packages that are not in the image"
+                config dependencies: (config dependencies
+                        select: [:dep | dep package hasWorkingCopy])].
+        (config dependencies allSatisfy: [:dep | dep isFulfilled])
+                ifFalse: [config upgradeUpToUpdate: anIntegerOrNil].!

Item was changed:
  ----- Method: MCMcmUpdater>>updateFromRepositories:upTo: (in category 'updating') -----
  updateFromRepositories: repositoryUrls upTo: versionNumber
+        "
+        MCMcmUpdater default updateFromRepositories: #('http://squeaksource.com/MCUpdateTest') upTo: 3
+        "
-        "MCMcmUpdater updateFromRepositories: #(
-                'http://squeaksource.com/MCUpdateTest'
-        )"
 
+        ^ self basicUpdateFromRepositories: repositoryUrls do: [:repo |
+                self updateFromRepository: repo upTo: versionNumber]!
-        | repos config |
-        MCConfiguration upgradeIsMerge: true.
-        "The list of repositories to consult in order"
-        repos := repositoryUrls collect:[:url|
-                MCRepositoryGroup default repositories
-                        detect:[:r| r description = url]
-                        ifNone:[ | r |
-                                r := MCHttpRepository location: url user: '' password: ''.
-                                MCRepositoryGroup default addRepository: r.
-                                r]].
-
-        "The list of updates-author.version.mcm sorted by version"
-        repos do:[ :r | config := self updateFromRepository: r upTo: versionNumber].
-        ^config!

Item was added:
+ ----- Method: MCMcmUpdater>>updateFromRepositories:upToUpdate: (in category 'updating') -----
+ updateFromRepositories: repositoryUrls upToUpdate: anIntegerOrNil
+        "
+        MCMcmUpdater default updateFromRepositories: #('http://squeaksource.com/MCUpdateTest') upToUpdate: 2130
+        "
+
+        ^ self basicUpdateFromRepositories: repositoryUrls do: [:repo |
+                self updateFromRepository: repo upToUpdate: anIntegerOrNil ]!

Item was changed:
  ----- Method: MCMcmUpdater>>updateFromRepository (in category 'updating') -----
  updateFromRepository
 
+        ^ self updateFromRepository: self getRepositoryFromRepositoryGroup!
-        | config repo |
-        repo := self getRepositoryFromRepositoryGroup.
-        repo cacheAllFileNamesDuring: [ | updateList |
-                updateList := self updateListFor: repo.
-                "Proceed only if there are updates available at all."
-                updateList ifNotEmpty: [
-                        updateList := self refreshUpdateMapFor: repo with: updateList.
-                        "Now process each update file. Check if we have all dependencies and if not,
-                        load the entire configuration (this is mostly to skip older updates quickly)"
-                        updateList do:[:assoc|
-                                ProgressNotification signal: '' extra: 'Processing ', assoc value.
-                                config := repo versionNamed: assoc value.
-                                self updateFromConfig: config.
-                                self lastUpdateMap at: repo description put: assoc key.
-                        ] displayingProgress: 'Processing configurations'.
-                        "We've loaded all the provided update configurations.
-                        Use the latest configuration to update all the remaining packages."
-                        (self useLatestPackagesFrom: repo) ifTrue: [
-                                config updateFromRepositories.
-                                config upgrade].
-                ]].
-        ^ config
- !

Item was added:
+ ----- Method: MCMcmUpdater>>updateFromRepository: (in category 'updating') -----
+ updateFromRepository: aRepository
+
+        ^ self basicUpdateFromRepository: aRepository thenDo: [:config |
+                "We've loaded all the provided update configurations. Use the latest configuration to update all the remaining packages."
+                (self useLatestPackagesFrom: aRepository) ifTrue: [
+                        config updateFromRepositories.
+                        config upgrade]]!

Item was changed:
  ----- Method: MCMcmUpdater>>updateFromRepository:upTo: (in category 'updating') -----
+ updateFromRepository: aRepository upTo: versionNumber
- updateFromRepository: repository upTo: versionNumber
 
+        ^ self
+                basicUpdateFromRepository: aRepository
+                whileUpdatesSatisfy: [:updateVersion | updateVersion <= versionNumber]
+                upToUpdate: nil
+                thenDo: []!
-        | config |
-        repository cacheAllFileNamesDuring: [ | updateList |
-                updateList := self updateListFor: repository.
-                "Proceed only if there are updates available at all."
-                updateList ifNotEmpty: [
-                        updateList := self refreshUpdateMapFor: repository with: updateList.
-                        "Now process each update file. Check if we have all dependencies and if not,
-                        load the entire configuration (this is mostly to skip older updates quickly)"
-                        updateList do:[:assoc|
-                                assoc key > versionNumber ifTrue: [^config].
-                                ProgressNotification signal: '' extra: 'Processing ', assoc value.
-                                config := repository versionNamed: assoc value.
-                                self updateFromConfig: config.
-                                self lastUpdateMap at: repository description put: assoc key.
-                        ] displayingProgress: 'Processing configurations'.
-                ]].
-        ^config
- !

Item was added:
+ ----- Method: MCMcmUpdater>>updateFromRepository:upToUpdate: (in category 'updating') -----
+ updateFromRepository: aRepository upToUpdate: anIntegerOrNil
+
+        ^ self
+                basicUpdateFromRepository: aRepository
+                whileUpdatesSatisfy: [true]
+                upToUpdate: anIntegerOrNil
+                thenDo: []!




Carpe Squeak!
Reply | Threaded
Open this post in threaded view
|

Re: The Inbox: MonticelloConfigurations-ct.166.mcz

codefrau
Hi Christoph,

Good idea, but is not quite that simple. The image "version number" does not uniquely identify a specific set of package versions. It's not enough to build a similar image again.

To have somewhat reproducible builds (they're not bit-identical) you need to store the actual list of loaded package versions. The easiest way to do that would be using an MCM. Only when an explicit config map is deployed that indeed is a "fixed point" with a defined set of packages.

MCMcmUpdater normally loads each explicit update map, plus then automatically updates the last map to the latest package versions. That is governed by the useLatestPackagesFrom: method.

The latest config map appears to be

A method for reproducible builds could take e.g. 'mt.485' as an argument and should produce an image with version number 20313:

((MCRepository trunk versionNamed: 'update-mt.485.mcm') dependencies collect: [:dep | dep versionInfo versionNumber]) sum


–Vanessa–


On Thu, Apr 8, 2021 at 1:37 PM Thiede, Christoph <[hidden email]> wrote:

Please review! :-)


A concrete use case I was having in mind for this feature is to allow arbitrary Squeak Trunk version numbers in smalltalkCI, see: https://github.com/hpi-swa/smalltalkCI/pull/506

At the moment, it is WIP - an unpolished prototype only, but the idea is that you could specify something like Squeak64-18242 instead of Squeak64-trunk in the CI/CLI args.


Nevertheless, even if the proposal for SCI should get rejected, I think this is a helpful feature because releases are quite rare in Squeak and I find it important to tag certain states in the Trunk evolution.

To improve support for the SCI PR, I would also like to backport these changes to 5.3 - there was only a single merge conflict, so if you agree with this proposal, I can send you a changeset for 5.3. Thanks in advance! :-)

Best,
Christoph

Von: Squeak-dev <[hidden email]> im Auftrag von [hidden email] <[hidden email]>
Gesendet: Donnerstag, 8. April 2021 22:30:32
An: [hidden email]
Betreff: [squeak-dev] The Inbox: MonticelloConfigurations-ct.166.mcz
 
A new version of MonticelloConfigurations was added to project The Inbox:
http://source.squeak.org/inbox/MonticelloConfigurations-ct.166.mcz

==================== Summary ====================

Name: MonticelloConfigurations-ct.166
Author: ct
Time: 8 April 2021, 10:30:30.702818 pm
UUID: 6d887a86-fe0f-8e4c-a878-e40e8e612918
Ancestors: MonticelloConfigurations-mt.165

Adds support in MCMcmUpdater to install updates up to a specified system version number. This can be helpful when you want to bring an image into an exact state of an alpha version. I.e., a bug report or a tool can refer to Squeak6.0 Alpha #19570. Now it's possible to bring your 5.3 image into this state by evaluating:

        MCMcmUpdater default doUpdate: true upToUpdate: 19570.

Still, it is possible to install all updates or navigate forward to a certain upstream version using the existing protocol:

        MCMcmUpdater default doUpdate: true.
        MCMcmUpdater default doUpdate: true upTo: 470.

Detailed changelog (anticlimax):

- Added 'upToUpdate:' variant of update selectors on MCMcmUpdater.
- Refactored existing update selectors to deduplicate the logic for doing a complete update (#doUpdate: etc.) versus installing all updates up to an upstream version (#doUpdate:upTo: etc.).
- Added #currentSystemVersion and #totalSystemVersion on MCConfiguration to determine the current resp. eventual system version as implied by the update stream.
- Renamed (+ deprecated) #depsSatisfying:version(s)Do:displayingProgress: due to misleading use of singular in the block keyword.
- Improved multilingual support for the updater.
- Improved documentation of the update protocol.

Note that in some situations, a small number of follow-up updates will be installed that go beyond the specified system version number. This is caused by interdependencies between the target version and its postdecessors when a definiton was moved from one package into another one. See sender of MCReorganizationPreloader.

=============== Diff against MonticelloConfigurations-mt.165 ===============

Item was added:
+ ----- Method: MCConfiguration class>>ensureOpenTranscript:during: (in category 'preferences') -----
+ ensureOpenTranscript: aBoolean during: aBlock
+
+        | previous |
+        previous := self ensureOpenTranscript.
+        self ensureOpenTranscript: aBoolean.
+        ^ aBlock ensure: [
+                self ensureOpenTranscript: previous]!

Item was added:
+ ----- Method: MCConfiguration>>currentSystemVersion (in category 'updating') -----
+ currentSystemVersion
+        "Answer the current system version found in my configuration (or the associated working copy)."
+
+        ^ self systemVersionForDeps: (self dependencies
+                collect: [:dep |
+                        dep isFulfilledByAncestors
+                                ifTrue: [dep versionInfo]
+                                ifFalse: [dep package workingCopy ancestors] ] )!

Item was added:
+ ----- Method: MCConfiguration>>depsSatisfying:upToUpdate:versionsDo:displayingProgress: (in category 'private') -----
+ depsSatisfying: selectBlock upToUpdate: anIntegerOrNil versionsDo: versionBlock displayingProgress: progressString
+
+        | count selectedVersions cleanWorkingCopies |
+        self cacheAllFileNamesDuring: [
+                self repositories do: [ :eachRepository |
+                        MCRepositoryGroup default addRepository: eachRepository ].
+               
+                "First, download selected versions"
+                count := 0.
+                selectedVersions := OrderedCollection new.
+                self withProgress: progressString in: self dependencies do: [ :dep | | verName repo |
+                        verName := dep versionInfo name.
+                        self class extraProgressInfo ifTrue:
+                                [ ProgressNotification signal: '' extra: ('Downloading {1}' translated format: {verName}) ].
+                        repo := self repositories
+                                detect: [ :eachRepository | eachRepository includesVersionNamed: verName ]
+                                ifNone:  [ self logError: ('Version {1} not found in any repository' translated format: {verName}).
+                                        self logError: 'Aborting' translated.
+                                        ^ count ].
+                        (selectBlock value: dep) ifTrue: [ | version |
+                                version := self versionNamed: verName for: dep from: repo.
+                                version ifNil: [ self logError: ('Could not download version {1} from {2}' translated format: {verName. repo description}).
+                                        self logError: 'Aborting' translated.
+                                        ^ count ].
+                                dep package workingCopy newRepositoryGroupIfDefault. "fix old working copies"
+                                dep package workingCopy repositoryGroup addRepository: repo.
+                                selectedVersions add: version ] ].
+               
+                "Then, process only those definitions that moved from one package to another, to avoid order dependence"
+                cleanWorkingCopies := MCWorkingCopy allManagers select:
+                        [ :wc | wc modified not and:
+                                [ selectedVersions anySatisfy: [ :v | wc package = v package ] ] ].
+                MCReorganizationPreloader preloadMovesBetween: selectedVersions.
+               
+                "Finally, load/merge selected versions"
+                [:exit | self withProgress: progressString in: selectedVersions do: [ :version |
+                        self logUpdate: version package with: version.
+                        self class extraProgressInfo ifTrue:
+                                [ ProgressNotification signal: '' extra: ('Installing {1}' translated format: {version info name}) ].
+                        versionBlock value: version.
+                        count := count + 1.
+                        anIntegerOrNil ifNotNil: [
+                                self currentSystemVersion highestUpdate < anIntegerOrNil ifFalse: exit.
+                                self flag: #optimization. "ct: By customizing the system version computation, we could save redundant version downloads above. However, because of the preloading stage, this might become a bit more sophisticated." ] ].
+                ] valueWithExit.
+               
+                "Clean up packages made dirty by MCReorganizationPreloader"
+                cleanWorkingCopies
+                        select: [ :wc | wc modified ]
+                        thenDo: [ :wc | wc checkModified ].
+        ].
+        ^ count!

Item was changed:
  ----- Method: MCConfiguration>>depsSatisfying:versionDo:displayingProgress: (in category 'private') -----
+ depsSatisfying: selectBlock versionDo: versionBlock displayingProgress: progressString
+
+        self deprecated.
+        ^ self
+                depsSatisfying: selectBlock
+                versionsDo: versionBlock
+                displayingProgress: progressString!
- depsSatisfying: selectBlock versionDo: verBlock displayingProgress: progressString
-        | count selectedVersions cleanWorkingCopies |
-        self cacheAllFileNamesDuring: [
-                self repositories do: [ :eachRepository |
-                        MCRepositoryGroup default addRepository: eachRepository ].
-                "First, download selected versions"
-                count := 0.
-                selectedVersions := OrderedCollection new.
-                self withProgress: progressString in: self dependencies do: [ :dep | | verName repo |
-                        verName := dep versionInfo name.
-                        self class extraProgressInfo ifTrue:
-                                [ ProgressNotification signal: '' extra: 'Downloading ' , verName ].
-                        repo := self repositories
-                                detect: [ :eachRepository | eachRepository includesVersionNamed: verName ]
-                                ifNone:  [ self logError: 'Version ' , verName , ' not found in any repository'.
-                                        self logError: 'Aborting'.
-                                        ^ count ].
-                        (selectBlock value: dep) ifTrue: [ | version |
-                                version := self versionNamed: verName for: dep from: repo.
-                                version ifNil: [ self logError: 'Could not download version ' , verName , ' from ' , repo description.
-                                        self logError: 'Aborting'.
-                                        ^ count ].
-                                dep package workingCopy newRepositoryGroupIfDefault. "fix old working copies"
-                                dep package workingCopy repositoryGroup addRepository: repo.
-                                selectedVersions add: version]].
-                "Then, process only those definitions that moved from one package to another, to avoid order dependence"
-                cleanWorkingCopies := MCWorkingCopy allManagers select:
-                        [ :wc | wc modified not and:
-                                [ selectedVersions anySatisfy: [ :v | wc package = v package ] ] ].
-                MCReorganizationPreloader preloadMovesBetween: selectedVersions.
-                "Finally, load/merge selected versions"
-                self withProgress: progressString in: selectedVersions do: [ :version |
-                        self logUpdate: version package with: version.
-                        self class extraProgressInfo ifTrue:
-                                [ ProgressNotification signal: '' extra: 'Installing ' , version info name ].
-                        verBlock value: version.
-                        count := count + 1 ].
-                "Clean up packages made dirty by MCReorganizationPreloader"
-                cleanWorkingCopies
-                        select: [ :wc | wc modified ]
-                        thenDo: [ :wc | wc checkModified ].
-        ].
-        ^ count!

Item was added:
+ ----- Method: MCConfiguration>>depsSatisfying:versionsDo:displayingProgress: (in category 'private') -----
+ depsSatisfying: selectBlock versionsDo: versionBlock displayingProgress: progressString
+
+        ^ self
+                depsSatisfying: selectBlock
+                upToUpdate: nil
+                versionsDo: versionBlock
+                displayingProgress: progressString!

Item was changed:
  ----- Method: MCConfiguration>>load (in category 'actions') -----
  load
         ^self depsSatisfying: [:dep | dep isCurrent not]
+                versionsDo: [:ver | ver load]
+                displayingProgress: 'loading packages' translated!
-                versionDo: [:ver | ver load]
-                displayingProgress: 'loading packages'
- !

Item was changed:
  ----- Method: MCConfiguration>>merge (in category 'actions') -----
  merge
         ^self depsSatisfying: [:dep | dep isFulfilledByAncestors not]
+                versionsDo: [:ver | ver merge]
+                displayingProgress: 'merging packages' translated!
-                versionDo: [:ver | ver merge]
-                displayingProgress: 'merging packages'
- !

Item was changed:
  ----- Method: MCConfiguration>>setSystemVersion (in category 'updating') -----
  setSystemVersion
-        "Set the current system version date to the latest date found in my configuration (or the associated working copy). Also set the highest update number to the sum of version numbers in my configuration."
 
+        | newVersion |
+        newVersion := self currentSystemVersion.
-        | versionNumbers versionDates |
-        versionNumbers := self dependencies collect: [:d |
-                (d versionInfo name copyAfterLast: $.) asInteger].
-        versionDates := self dependencies collect: [:d |
-                d versionInfo date
-                        ifNil: [d package workingCopy ancestors first date]].
         SystemVersion current
+                date: newVersion date;
+                highestUpdate: newVersion highestUpdate.!
-                date: versionDates max;
-                highestUpdate: versionNumbers sum.!

Item was added:
+ ----- Method: MCConfiguration>>systemVersionForDeps: (in category 'updating') -----
+ systemVersionForDeps: dependencies
+        "Answer the system version that will be reached by loading the passed dependencies."
+
+        | versionNumbers versionDates |
+        versionNumbers := dependencies collect: [:dep |
+                dep isCollection
+                        ifFalse: [dep name versionNumber]
+                        ifTrue: [(dep collect: [:version |
+                                version name versionNumber])
+                                        ifEmpty: [0]
+                                        ifNotEmpty: #max]].
+        versionDates := dependencies with: self dependencies collect: [:dep :originalDep |
+                (dep isCollection ifFalse: [dep date])
+                        ifNil: [originalDep package workingCopy ancestors
+                                        ifEmpty: [nil]
+                                        ifNotEmpty: [:ancestors | ancestors first date]]].
+       
+        ^ SystemVersion new
+                date: ((versionDates copyWithout: nil)
+                        ifEmpty: [nil]
+                        ifNotEmpty: #max);
+                highestUpdate: versionNumbers sum!

Item was added:
+ ----- Method: MCConfiguration>>totalSystemVersion (in category 'updating') -----
+ totalSystemVersion
+        "Answer the total system version that will be reached by upgrading the receiver."
+
+        ^ self systemVersionForDeps: (self dependencies collect: #versionInfo)!

Item was changed:
  ----- Method: MCConfiguration>>upgrade (in category 'actions') -----
  upgrade
+
+        ^ self upgradeUpToUpdate: nil!
-        ^self depsSatisfying:
-                        [:dep | dep isFulfilledByAncestors not]
-                versionDo:
-                        [:ver |
-                        (self class upgradeIsMerge and: [ver shouldMerge])
-                                ifFalse: [ver load]
-                                ifTrue:
-                                        [[ver merge]
-                                                on: MCNoChangesException
-                                                do: [:req| req resume ]
-                                                on: MCMergeResolutionRequest
-                                                do: [:request |
-                                                        request merger conflicts isEmpty
-                                                                ifTrue: [request resume: true]
-                                                                ifFalse: [request pass]]
-                                                on: Deprecation
-                                                do: [:req| req resume ]]]
-                displayingProgress: 'upgrading packages'!

Item was added:
+ ----- Method: MCConfiguration>>upgradeUpToUpdate: (in category 'actions') -----
+ upgradeUpToUpdate: anIntegerOrNil
+
+        ^ self
+                depsSatisfying: [:dep |
+                        dep isFulfilledByAncestors not]
+                upToUpdate: anIntegerOrNil
+                versionsDo: [:ver |
+                        (self class upgradeIsMerge and: [ver shouldMerge])
+                                ifFalse: [ver load]
+                                ifTrue:
+                                        [[ver merge]
+                                                on: MCNoChangesException
+                                                do: [:req | req resume ]
+                                                on: MCMergeResolutionRequest
+                                                do: [:request |
+                                                        request merger conflicts isEmpty
+                                                                ifTrue: [request resume: true]
+                                                                ifFalse: [request pass]]
+                                                on: Deprecation
+                                                do: [:req | req resume ]]]
+                displayingProgress: 'upgrading packages' translated!

Item was added:
+ ----- Method: MCMcmUpdater>>basicDoUpdate:do: (in category 'updating') -----
+ basicDoUpdate: interactive do: aBlock
+        "Update the image by evaluating aBlock. If this is the default updater for the system, update the system version when complete. If interactive, use a modal notifier, otherwise only update the transcript. Flush all caches. If a previous download failed, this is often helpful."
+
+        | config previousUpdateLevel |
+        previousUpdateLevel := SystemVersion current highestUpdate.
+        MCFileBasedRepository flushAllCaches.
+       
+        MCConfiguration ensureOpenTranscript: interactive during: [
+                config := aBlock value.
+       
+                config ifNil: [
+                        interactive ifTrue: [^ self inform: 'Unable to retrieve updates from remote repository.' translated].
+                        Transcript cr; show: '==========  Unable to retrieve updates from remote repository. ==========' translated; cr.
+                        ^ self ].
+       
+                MCMcmUpdater default == self
+                        ifTrue: [
+                                config setSystemVersion.
+                                interactive ifTrue: [
+                                        self inform: (self updateMessageFor: previousUpdateLevel)].
+                                Transcript cr;
+                                        show: '==========  Update completed:  ' translated;
+                                        show: previousUpdateLevel;
+                                        show: ' -> ' ;
+                                        show: SystemVersion current highestUpdate;
+                                        show: ' =========='; cr ]
+                        ifFalse: [
+                                interactive
+                                        ifTrue: [ self inform: 'Update completed.' ].
+                                Transcript cr; show: '==========  Update completed. ==========' translated; cr ] ].!

Item was added:
+ ----- Method: MCMcmUpdater>>basicUpdateFromRepositories:do: (in category 'updating') -----
+ basicUpdateFromRepositories: repositoryUrls do: repositoryBlock
+
+        | repos config |
+        MCConfiguration upgradeIsMerge: true.
+        "The list of repositories to consult in order"
+        repos := repositoryUrls collect: [:url| 
+                MCRepositoryGroup default repositories
+                        detect: [:repo | repo description = url]
+                        ifNone: [ | repo |
+                                repo := MCHttpRepository location: url.
+                                MCRepositoryGroup default addRepository: repo.
+                                repo]].
+       
+        "The list of updates-author.version.mcm sorted by version"
+        repos do: [:repo |
+                config := repositoryBlock value: repo].
+        ^ config!

Item was added:
+ ----- Method: MCMcmUpdater>>basicUpdateFromRepository:thenDo: (in category 'updating') -----
+ basicUpdateFromRepository: aRepository thenDo: configBlock
+
+        ^ self
+                basicUpdateFromRepository: aRepository
+                whileUpdatesSatisfy: [true]
+                upToUpdate: nil
+                thenDo: configBlock!

Item was added:
+ ----- Method: MCMcmUpdater>>basicUpdateFromRepository:whileUpdatesSatisfy:upToUpdate:thenDo: (in category 'updating') -----
+ basicUpdateFromRepository: aRepository whileUpdatesSatisfy: updateBlock upToUpdate: anIntegerOrNil thenDo: configBlock
+
+        | config |
+        aRepository cacheAllFileNamesDuring: [ | updateList |
+                updateList := self updateListFor: aRepository.
+                "Proceed only if there are updates available at all."
+                updateList ifNotEmpty: [
+                        updateList := self refreshUpdateMapFor: aRepository with: updateList.
+                        "Now process each update file. Check if we have all dependencies and if not, load the entire configuration (this is mostly to skip older updates quickly)"
+                        [:exit | updateList
+                                do: [:assoc |
+                                        (updateBlock cull: assoc key cull: assoc value) ifFalse: exit.
+                                        ProgressNotification signal: '' extra: ('Processing {1}' translated format: {assoc value}).
+                                        config := aRepository versionNamed: assoc value.
+                                        anIntegerOrNil ifNotNil: [
+                                                config currentSystemVersion highestUpdate <= anIntegerOrNil ifFalse: exit].
+                                        self updateFromConfig: config upToUpdate: anIntegerOrNil.
+                                        anIntegerOrNil ifNotNil: [
+                                                config currentSystemVersion highestUpdate < config totalSystemVersion highestUpdate ifTrue: exit].
+                                        self lastUpdateMap at: aRepository description put: assoc key]
+                                displayingProgress: 'Processing configurations' translated] valueWithExit.
+                        configBlock cull: config]].
+        ^ config!

Item was changed:
  ----- Method: MCMcmUpdater>>doUpdate: (in category 'updating') -----
  doUpdate: interactive
+        "Update the image by loading all pending updates from the server."
-        "Update the image by loading all pending updates from the server. If this is
-        the default updater for the system, update the system version when complete.
-        If interteractive use a modal notifier, otherwise only update the transcript.
-        Flush all caches. If a previous download failed this is often helpful"
 
+        ^ self basicDoUpdate: interactive do: [
+                self updateFromRepository ]!
-        | config previousUpdateLevel ensureTranscriptSetting |
-        previousUpdateLevel := SystemVersion current highestUpdate.
-        MCFileBasedRepository flushAllCaches.  
-        ensureTranscriptSetting := MCConfiguration ensureOpenTranscript.
-        [ MCConfiguration ensureOpenTranscript: interactive.
-        config := self updateFromRepository.
-        config ifNil: [
-                interactive ifTrue: [ ^self inform: 'Unable to retrieve updates from remote repository.' translated ].
-                Transcript cr; show: '==========  Unable to retrieve updates from remote repository. ==========' translated; cr.
-                ^ self ].
-        MCMcmUpdater default == self
-                ifTrue: [
-                        config setSystemVersion.
-                        interactive ifTrue: [
-                                self inform: (self updateMessageFor: previousUpdateLevel)].
-                        Transcript cr;
-                                show: '==========  Update completed:  ' translated;
-                                show: previousUpdateLevel;
-                                show: ' -> ' ;
-                                show: SystemVersion current highestUpdate;
-                                show: ' =========='; cr ]
-                ifFalse: [
-                        interactive
-                                ifTrue: [ self inform: 'Update completed.' ].
-                        Transcript cr; show: '==========  Update completed. ==========' translated; cr ] ]
-                ensure: [ MCConfiguration ensureOpenTranscript: ensureTranscriptSetting].
-
-        !

Item was changed:
  ----- Method: MCMcmUpdater>>doUpdate:upTo: (in category 'updating') -----
  doUpdate: interactive upTo: versionNumber
+        "Update the image by loading all pending updates from the server until update stream with the specified version number has been processed."
-        "Update the image by loading all pending updates from the server. If this is
-        the default updater for the system, update the system version when complete.
-        If interteractive use a modal notifier, otherwise only update the transcript.
-        Flush all caches. If a previous download failed this is often helpful"
 
+        ^ self basicDoUpdate: interactive do: [
+                self updateFromRepositories: {self repository} upTo: versionNumber ]!
-        | config previousUpdateLevel |
-        previousUpdateLevel := SystemVersion current highestUpdate.
-        MCFileBasedRepository flushAllCaches.
-        config := self updateFromRepositories: { self repository } upTo: versionNumber.
-        config ifNil: [
-                interactive ifTrue: [ ^self inform: 'Unable to retrieve updates from remote repository.' translated ].
-                Transcript cr; show: '==========  Unable to retrieve updates from remote repository. ==========' translated; cr.
-                ^ self ].
-        MCMcmUpdater default == self
-                ifTrue: [
-                        config setSystemVersion.
-                        interactive ifTrue: [
-                                self inform: (self updateMessageFor: previousUpdateLevel)].
-                        Transcript cr;
-                                show: '==========  Update completed:  ' translated;
-                                show: previousUpdateLevel;
-                                show: ' -> ' ;
-                                show: SystemVersion current highestUpdate;
-                                show: ' =========='; cr ]
-                ifFalse: [
-                        interactive
-                                ifTrue: [ self inform: 'Update completed.' ].
-                        Transcript cr; show: '==========  Update completed. ==========' translated; cr ]
-        !

Item was added:
+ ----- Method: MCMcmUpdater>>doUpdate:upToUpdate: (in category 'updating') -----
+ doUpdate: interactive upToUpdate: anIntegerOrNil
+        "Update the image by loading all pending updates from the server until the system version has reached at least anInteger. If anInteger is nil, install all available updates."
+
+        ^ self basicDoUpdate: interactive do: [
+                self updateFromRepositories: {self repository} upToUpdate: anIntegerOrNil ]!

Item was changed:
  ----- Method: MCMcmUpdater>>doUpdateUpTo: (in category 'updating') -----
  doUpdateUpTo: versionNumber
+        "Update the image by loading all pending updates from the server until update stream with the specified version number has been processed."
-        "Update the image by loading all pending updates from the server. If this is
-        the default updater for the system, update the system version when complete.
-        Flush all caches. If a previous download failed this is often helpful"
 
         ^self doUpdate: true upTo: versionNumber
  !

Item was changed:
  ----- Method: MCMcmUpdater>>updateFromConfig: (in category 'updating') -----
  updateFromConfig: config
 
+        ^ self updateFromConfig: config upToUpdate: nil!
-        "Skip packages that were specifically unloaded"
-        config dependencies: (config dependencies
-                reject: [:dep| self class skipPackages includes: dep package name]).
-        self class updateMissingPackages ifFalse:[
-                "Skip packages that are not in the image"
-                config dependencies: (config dependencies
-                        select: [:dep| dep package hasWorkingCopy])].
-        (config dependencies allSatisfy:[:dep| dep isFulfilled])
-                ifFalse:[config upgrade].
- !

Item was added:
+ ----- Method: MCMcmUpdater>>updateFromConfig:upToUpdate: (in category 'updating') -----
+ updateFromConfig: config upToUpdate: anIntegerOrNil
+
+        "Skip packages that were specifically unloaded"
+        config dependencies: (config dependencies
+                reject: [:dep | self class skipPackages includes: dep package name]).
+        self class updateMissingPackages ifFalse: [
+                "Skip packages that are not in the image"
+                config dependencies: (config dependencies
+                        select: [:dep | dep package hasWorkingCopy])].
+        (config dependencies allSatisfy: [:dep | dep isFulfilled])
+                ifFalse: [config upgradeUpToUpdate: anIntegerOrNil].!

Item was changed:
  ----- Method: MCMcmUpdater>>updateFromRepositories:upTo: (in category 'updating') -----
  updateFromRepositories: repositoryUrls upTo: versionNumber
+        "
+        MCMcmUpdater default updateFromRepositories: #('http://squeaksource.com/MCUpdateTest') upTo: 3
+        "
-        "MCMcmUpdater updateFromRepositories: #(
-                'http://squeaksource.com/MCUpdateTest'
-        )"
 
+        ^ self basicUpdateFromRepositories: repositoryUrls do: [:repo |
+                self updateFromRepository: repo upTo: versionNumber]!
-        | repos config |
-        MCConfiguration upgradeIsMerge: true.
-        "The list of repositories to consult in order"
-        repos := repositoryUrls collect:[:url|
-                MCRepositoryGroup default repositories
-                        detect:[:r| r description = url]
-                        ifNone:[ | r |
-                                r := MCHttpRepository location: url user: '' password: ''.
-                                MCRepositoryGroup default addRepository: r.
-                                r]].
-
-        "The list of updates-author.version.mcm sorted by version"
-        repos do:[ :r | config := self updateFromRepository: r upTo: versionNumber].
-        ^config!

Item was added:
+ ----- Method: MCMcmUpdater>>updateFromRepositories:upToUpdate: (in category 'updating') -----
+ updateFromRepositories: repositoryUrls upToUpdate: anIntegerOrNil
+        "
+        MCMcmUpdater default updateFromRepositories: #('http://squeaksource.com/MCUpdateTest') upToUpdate: 2130
+        "
+
+        ^ self basicUpdateFromRepositories: repositoryUrls do: [:repo |
+                self updateFromRepository: repo upToUpdate: anIntegerOrNil ]!

Item was changed:
  ----- Method: MCMcmUpdater>>updateFromRepository (in category 'updating') -----
  updateFromRepository
 
+        ^ self updateFromRepository: self getRepositoryFromRepositoryGroup!
-        | config repo |
-        repo := self getRepositoryFromRepositoryGroup.
-        repo cacheAllFileNamesDuring: [ | updateList |
-                updateList := self updateListFor: repo.
-                "Proceed only if there are updates available at all."
-                updateList ifNotEmpty: [
-                        updateList := self refreshUpdateMapFor: repo with: updateList.
-                        "Now process each update file. Check if we have all dependencies and if not,
-                        load the entire configuration (this is mostly to skip older updates quickly)"
-                        updateList do:[:assoc|
-                                ProgressNotification signal: '' extra: 'Processing ', assoc value.
-                                config := repo versionNamed: assoc value.
-                                self updateFromConfig: config.
-                                self lastUpdateMap at: repo description put: assoc key.
-                        ] displayingProgress: 'Processing configurations'.
-                        "We've loaded all the provided update configurations.
-                        Use the latest configuration to update all the remaining packages."
-                        (self useLatestPackagesFrom: repo) ifTrue: [
-                                config updateFromRepositories.
-                                config upgrade].
-                ]].
-        ^ config
- !

Item was added:
+ ----- Method: MCMcmUpdater>>updateFromRepository: (in category 'updating') -----
+ updateFromRepository: aRepository
+
+        ^ self basicUpdateFromRepository: aRepository thenDo: [:config |
+                "We've loaded all the provided update configurations. Use the latest configuration to update all the remaining packages."
+                (self useLatestPackagesFrom: aRepository) ifTrue: [
+                        config updateFromRepositories.
+                        config upgrade]]!

Item was changed:
  ----- Method: MCMcmUpdater>>updateFromRepository:upTo: (in category 'updating') -----
+ updateFromRepository: aRepository upTo: versionNumber
- updateFromRepository: repository upTo: versionNumber
 
+        ^ self
+                basicUpdateFromRepository: aRepository
+                whileUpdatesSatisfy: [:updateVersion | updateVersion <= versionNumber]
+                upToUpdate: nil
+                thenDo: []!
-        | config |
-        repository cacheAllFileNamesDuring: [ | updateList |
-                updateList := self updateListFor: repository.
-                "Proceed only if there are updates available at all."
-                updateList ifNotEmpty: [
-                        updateList := self refreshUpdateMapFor: repository with: updateList.
-                        "Now process each update file. Check if we have all dependencies and if not,
-                        load the entire configuration (this is mostly to skip older updates quickly)"
-                        updateList do:[:assoc|
-                                assoc key > versionNumber ifTrue: [^config].
-                                ProgressNotification signal: '' extra: 'Processing ', assoc value.
-                                config := repository versionNamed: assoc value.
-                                self updateFromConfig: config.
-                                self lastUpdateMap at: repository description put: assoc key.
-                        ] displayingProgress: 'Processing configurations'.
-                ]].
-        ^config
- !

Item was added:
+ ----- Method: MCMcmUpdater>>updateFromRepository:upToUpdate: (in category 'updating') -----
+ updateFromRepository: aRepository upToUpdate: anIntegerOrNil
+
+        ^ self
+                basicUpdateFromRepository: aRepository
+                whileUpdatesSatisfy: [true]
+                upToUpdate: anIntegerOrNil
+                thenDo: []!





Reply | Threaded
Open this post in threaded view
|

Re: The Inbox: MonticelloConfigurations-ct.166.mcz

marcel.taeumel
Hi Christoph,

as Vanessa explained, updates up to a specific MCM in the current update map might work. This also would not apply to cross-release/trunk updates in general. But might work most of the time since we rarely update release repositories.

Since we now also have comments in MCMs, a user might actually be able to pick the desired MCM from a list of not-yet-loaded ones.

(Hmm... maybe approximating a list of versions from interpolating the build version between two MCMs might work ...)

Best,
Marcel

Am 15.04.2021 02:13:34 schrieb Vanessa Freudenberg <[hidden email]>:

Hi Christoph,

Good idea, but is not quite that simple. The image "version number" does not uniquely identify a specific set of package versions. It's not enough to build a similar image again.

To have somewhat reproducible builds (they're not bit-identical) you need to store the actual list of loaded package versions. The easiest way to do that would be using an MCM. Only when an explicit config map is deployed that indeed is a "fixed point" with a defined set of packages.

MCMcmUpdater normally loads each explicit update map, plus then automatically updates the last map to the latest package versions. That is governed by the useLatestPackagesFrom: method.

The latest config map appears to be

A method for reproducible builds could take e.g. 'mt.485' as an argument and should produce an image with version number 20313:

((MCRepository trunk versionNamed: 'update-mt.485.mcm') dependencies collect: [:dep | dep versionInfo versionNumber]) sum


–Vanessa–


On Thu, Apr 8, 2021 at 1:37 PM Thiede, Christoph <[hidden email]> wrote:

Please review! :-)


A concrete use case I was having in mind for this feature is to allow arbitrary Squeak Trunk version numbers in smalltalkCI, see: https://github.com/hpi-swa/smalltalkCI/pull/506

At the moment, it is WIP - an unpolished prototype only, but the idea is that you could specify something like Squeak64-18242 instead of Squeak64-trunk in the CI/CLI args.


Nevertheless, even if the proposal for SCI should get rejected, I think this is a helpful feature because releases are quite rare in Squeak and I find it important to tag certain states in the Trunk evolution.

To improve support for the SCI PR, I would also like to backport these changes to 5.3 - there was only a single merge conflict, so if you agree with this proposal, I can send you a changeset for 5.3. Thanks in advance! :-)

Best,
Christoph

Von: Squeak-dev <[hidden email]> im Auftrag von [hidden email] <[hidden email]>
Gesendet: Donnerstag, 8. April 2021 22:30:32
An: [hidden email]
Betreff: [squeak-dev] The Inbox: MonticelloConfigurations-ct.166.mcz
 
A new version of MonticelloConfigurations was added to project The Inbox:
http://source.squeak.org/inbox/MonticelloConfigurations-ct.166.mcz

==================== Summary ====================

Name: MonticelloConfigurations-ct.166
Author: ct
Time: 8 April 2021, 10:30:30.702818 pm
UUID: 6d887a86-fe0f-8e4c-a878-e40e8e612918
Ancestors: MonticelloConfigurations-mt.165

Adds support in MCMcmUpdater to install updates up to a specified system version number. This can be helpful when you want to bring an image into an exact state of an alpha version. I.e., a bug report or a tool can refer to Squeak6.0 Alpha #19570. Now it's possible to bring your 5.3 image into this state by evaluating:

        MCMcmUpdater default doUpdate: true upToUpdate: 19570.

Still, it is possible to install all updates or navigate forward to a certain upstream version using the existing protocol:

        MCMcmUpdater default doUpdate: true.
        MCMcmUpdater default doUpdate: true upTo: 470.

Detailed changelog (anticlimax):

- Added 'upToUpdate:' variant of update selectors on MCMcmUpdater.
- Refactored existing update selectors to deduplicate the logic for doing a complete update (#doUpdate: etc.) versus installing all updates up to an upstream version (#doUpdate:upTo: etc.).
- Added #currentSystemVersion and #totalSystemVersion on MCConfiguration to determine the current resp. eventual system version as implied by the update stream.
- Renamed (+ deprecated) #depsSatisfying:version(s)Do:displayingProgress: due to misleading use of singular in the block keyword.
- Improved multilingual support for the updater.
- Improved documentation of the update protocol.

Note that in some situations, a small number of follow-up updates will be installed that go beyond the specified system version number. This is caused by interdependencies between the target version and its postdecessors when a definiton was moved from one package into another one. See sender of MCReorganizationPreloader.

=============== Diff against MonticelloConfigurations-mt.165 ===============

Item was added:
+ ----- Method: MCConfiguration class>>ensureOpenTranscript:during: (in category 'preferences') -----
+ ensureOpenTranscript: aBoolean during: aBlock
+
+        | previous |
+        previous := self ensureOpenTranscript.
+        self ensureOpenTranscript: aBoolean.
+        ^ aBlock ensure: [
+                self ensureOpenTranscript: previous]!

Item was added:
+ ----- Method: MCConfiguration>>currentSystemVersion (in category 'updating') -----
+ currentSystemVersion
+        "Answer the current system version found in my configuration (or the associated working copy)."
+
+        ^ self systemVersionForDeps: (self dependencies
+                collect: [:dep |
+                        dep isFulfilledByAncestors
+                                ifTrue: [dep versionInfo]
+                                ifFalse: [dep package workingCopy ancestors] ] )!

Item was added:
+ ----- Method: MCConfiguration>>depsSatisfying:upToUpdate:versionsDo:displayingProgress: (in category 'private') -----
+ depsSatisfying: selectBlock upToUpdate: anIntegerOrNil versionsDo: versionBlock displayingProgress: progressString
+
+        | count selectedVersions cleanWorkingCopies |
+        self cacheAllFileNamesDuring: [
+                self repositories do: [ :eachRepository |
+                        MCRepositoryGroup default addRepository: eachRepository ].
+               
+                "First, download selected versions"
+                count := 0.
+                selectedVersions := OrderedCollection new.
+                self withProgress: progressString in: self dependencies do: [ :dep | | verName repo |
+                        verName := dep versionInfo name.
+                        self class extraProgressInfo ifTrue:
+                                [ ProgressNotification signal: '' extra: ('Downloading {1}' translated format: {verName}) ].
+                        repo := self repositories
+                                detect: [ :eachRepository | eachRepository includesVersionNamed: verName ]
+                                ifNone:  [ self logError: ('Version {1} not found in any repository' translated format: {verName}).
+                                        self logError: 'Aborting' translated.
+                                        ^ count ].
+                        (selectBlock value: dep) ifTrue: [ | version |
+                                version := self versionNamed: verName for: dep from: repo.
+                                version ifNil: [ self logError: ('Could not download version {1} from {2}' translated format: {verName. repo description}).
+                                        self logError: 'Aborting' translated.
+                                        ^ count ].
+                                dep package workingCopy newRepositoryGroupIfDefault. "fix old working copies"
+                                dep package workingCopy repositoryGroup addRepository: repo.
+                                selectedVersions add: version ] ].
+               
+                "Then, process only those definitions that moved from one package to another, to avoid order dependence"
+                cleanWorkingCopies := MCWorkingCopy allManagers select:
+                        [ :wc | wc modified not and:
+                                [ selectedVersions anySatisfy: [ :v | wc package = v package ] ] ].
+                MCReorganizationPreloader preloadMovesBetween: selectedVersions.
+               
+                "Finally, load/merge selected versions"
+                [:exit | self withProgress: progressString in: selectedVersions do: [ :version |
+                        self logUpdate: version package with: version.
+                        self class extraProgressInfo ifTrue:
+                                [ ProgressNotification signal: '' extra: ('Installing {1}' translated format: {version info name}) ].
+                        versionBlock value: version.
+                        count := count + 1.
+                        anIntegerOrNil ifNotNil: [
+                                self currentSystemVersion highestUpdate < anIntegerOrNil ifFalse: exit.
+                                self flag: #optimization. "ct: By customizing the system version computation, we could save redundant version downloads above. However, because of the preloading stage, this might become a bit more sophisticated." ] ].
+                ] valueWithExit.
+               
+                "Clean up packages made dirty by MCReorganizationPreloader"
+                cleanWorkingCopies
+                        select: [ :wc | wc modified ]
+                        thenDo: [ :wc | wc checkModified ].
+        ].
+        ^ count!

Item was changed:
  ----- Method: MCConfiguration>>depsSatisfying:versionDo:displayingProgress: (in category 'private') -----
+ depsSatisfying: selectBlock versionDo: versionBlock displayingProgress: progressString
+
+        self deprecated.
+        ^ self
+                depsSatisfying: selectBlock
+                versionsDo: versionBlock
+                displayingProgress: progressString!
- depsSatisfying: selectBlock versionDo: verBlock displayingProgress: progressString
-        | count selectedVersions cleanWorkingCopies |
-        self cacheAllFileNamesDuring: [
-                self repositories do: [ :eachRepository |
-                        MCRepositoryGroup default addRepository: eachRepository ].
-                "First, download selected versions"
-                count := 0.
-                selectedVersions := OrderedCollection new.
-                self withProgress: progressString in: self dependencies do: [ :dep | | verName repo |
-                        verName := dep versionInfo name.
-                        self class extraProgressInfo ifTrue:
-                                [ ProgressNotification signal: '' extra: 'Downloading ' , verName ].
-                        repo := self repositories
-                                detect: [ :eachRepository | eachRepository includesVersionNamed: verName ]
-                                ifNone:  [ self logError: 'Version ' , verName , ' not found in any repository'.
-                                        self logError: 'Aborting'.
-                                        ^ count ].
-                        (selectBlock value: dep) ifTrue: [ | version |
-                                version := self versionNamed: verName for: dep from: repo.
-                                version ifNil: [ self logError: 'Could not download version ' , verName , ' from ' , repo description.
-                                        self logError: 'Aborting'.
-                                        ^ count ].
-                                dep package workingCopy newRepositoryGroupIfDefault. "fix old working copies"
-                                dep package workingCopy repositoryGroup addRepository: repo.
-                                selectedVersions add: version]].
-                "Then, process only those definitions that moved from one package to another, to avoid order dependence"
-                cleanWorkingCopies := MCWorkingCopy allManagers select:
-                        [ :wc | wc modified not and:
-                                [ selectedVersions anySatisfy: [ :v | wc package = v package ] ] ].
-                MCReorganizationPreloader preloadMovesBetween: selectedVersions.
-                "Finally, load/merge selected versions"
-                self withProgress: progressString in: selectedVersions do: [ :version |
-                        self logUpdate: version package with: version.
-                        self class extraProgressInfo ifTrue:
-                                [ ProgressNotification signal: '' extra: 'Installing ' , version info name ].
-                        verBlock value: version.
-                        count := count + 1 ].
-                "Clean up packages made dirty by MCReorganizationPreloader"
-                cleanWorkingCopies
-                        select: [ :wc | wc modified ]
-                        thenDo: [ :wc | wc checkModified ].
-        ].
-        ^ count!

Item was added:
+ ----- Method: MCConfiguration>>depsSatisfying:versionsDo:displayingProgress: (in category 'private') -----
+ depsSatisfying: selectBlock versionsDo: versionBlock displayingProgress: progressString
+
+        ^ self
+                depsSatisfying: selectBlock
+                upToUpdate: nil
+                versionsDo: versionBlock
+                displayingProgress: progressString!

Item was changed:
  ----- Method: MCConfiguration>>load (in category 'actions') -----
  load
         ^self depsSatisfying: [:dep | dep isCurrent not]
+                versionsDo: [:ver | ver load]
+                displayingProgress: 'loading packages' translated!
-                versionDo: [:ver | ver load]
-                displayingProgress: 'loading packages'
- !

Item was changed:
  ----- Method: MCConfiguration>>merge (in category 'actions') -----
  merge
         ^self depsSatisfying: [:dep | dep isFulfilledByAncestors not]
+                versionsDo: [:ver | ver merge]
+                displayingProgress: 'merging packages' translated!
-                versionDo: [:ver | ver merge]
-                displayingProgress: 'merging packages'
- !

Item was changed:
  ----- Method: MCConfiguration>>setSystemVersion (in category 'updating') -----
  setSystemVersion
-        "Set the current system version date to the latest date found in my configuration (or the associated working copy). Also set the highest update number to the sum of version numbers in my configuration."
 
+        | newVersion |
+        newVersion := self currentSystemVersion.
-        | versionNumbers versionDates |
-        versionNumbers := self dependencies collect: [:d |
-                (d versionInfo name copyAfterLast: $.) asInteger].
-        versionDates := self dependencies collect: [:d |
-                d versionInfo date
-                        ifNil: [d package workingCopy ancestors first date]].
         SystemVersion current
+                date: newVersion date;
+                highestUpdate: newVersion highestUpdate.!
-                date: versionDates max;
-                highestUpdate: versionNumbers sum.!

Item was added:
+ ----- Method: MCConfiguration>>systemVersionForDeps: (in category 'updating') -----
+ systemVersionForDeps: dependencies
+        "Answer the system version that will be reached by loading the passed dependencies."
+
+        | versionNumbers versionDates |
+        versionNumbers := dependencies collect: [:dep |
+                dep isCollection
+                        ifFalse: [dep name versionNumber]
+                        ifTrue: [(dep collect: [:version |
+                                version name versionNumber])
+                                        ifEmpty: [0]
+                                        ifNotEmpty: #max]].
+        versionDates := dependencies with: self dependencies collect: [:dep :originalDep |
+                (dep isCollection ifFalse: [dep date])
+                        ifNil: [originalDep package workingCopy ancestors
+                                        ifEmpty: [nil]
+                                        ifNotEmpty: [:ancestors | ancestors first date]]].
+       
+        ^ SystemVersion new
+                date: ((versionDates copyWithout: nil)
+                        ifEmpty: [nil]
+                        ifNotEmpty: #max);
+                highestUpdate: versionNumbers sum!

Item was added:
+ ----- Method: MCConfiguration>>totalSystemVersion (in category 'updating') -----
+ totalSystemVersion
+        "Answer the total system version that will be reached by upgrading the receiver."
+
+        ^ self systemVersionForDeps: (self dependencies collect: #versionInfo)!

Item was changed:
  ----- Method: MCConfiguration>>upgrade (in category 'actions') -----
  upgrade
+
+        ^ self upgradeUpToUpdate: nil!
-        ^self depsSatisfying:
-                        [:dep | dep isFulfilledByAncestors not]
-                versionDo:
-                        [:ver |
-                        (self class upgradeIsMerge and: [ver shouldMerge])
-                                ifFalse: [ver load]
-                                ifTrue:
-                                        [[ver merge]
-                                                on: MCNoChangesException
-                                                do: [:req| req resume ]
-                                                on: MCMergeResolutionRequest
-                                                do: [:request |
-                                                        request merger conflicts isEmpty
-                                                                ifTrue: [request resume: true]
-                                                                ifFalse: [request pass]]
-                                                on: Deprecation
-                                                do: [:req| req resume ]]]
-                displayingProgress: 'upgrading packages'!

Item was added:
+ ----- Method: MCConfiguration>>upgradeUpToUpdate: (in category 'actions') -----
+ upgradeUpToUpdate: anIntegerOrNil
+
+        ^ self
+                depsSatisfying: [:dep |
+                        dep isFulfilledByAncestors not]
+                upToUpdate: anIntegerOrNil
+                versionsDo: [:ver |
+                        (self class upgradeIsMerge and: [ver shouldMerge])
+                                ifFalse: [ver load]
+                                ifTrue:
+                                        [[ver merge]
+                                                on: MCNoChangesException
+                                                do: [:req | req resume ]
+                                                on: MCMergeResolutionRequest
+                                                do: [:request |
+                                                        request merger conflicts isEmpty
+                                                                ifTrue: [request resume: true]
+                                                                ifFalse: [request pass]]
+                                                on: Deprecation
+                                                do: [:req | req resume ]]]
+                displayingProgress: 'upgrading packages' translated!

Item was added:
+ ----- Method: MCMcmUpdater>>basicDoUpdate:do: (in category 'updating') -----
+ basicDoUpdate: interactive do: aBlock
+        "Update the image by evaluating aBlock. If this is the default updater for the system, update the system version when complete. If interactive, use a modal notifier, otherwise only update the transcript. Flush all caches. If a previous download failed, this is often helpful."
+
+        | config previousUpdateLevel |
+        previousUpdateLevel := SystemVersion current highestUpdate.
+        MCFileBasedRepository flushAllCaches.
+       
+        MCConfiguration ensureOpenTranscript: interactive during: [
+                config := aBlock value.
+       
+                config ifNil: [
+                        interactive ifTrue: [^ self inform: 'Unable to retrieve updates from remote repository.' translated].
+                        Transcript cr; show: '==========  Unable to retrieve updates from remote repository. ==========' translated; cr.
+                        ^ self ].
+       
+                MCMcmUpdater default == self
+                        ifTrue: [
+                                config setSystemVersion.
+                                interactive ifTrue: [
+                                        self inform: (self updateMessageFor: previousUpdateLevel)].
+                                Transcript cr;
+                                        show: '==========  Update completed:  ' translated;
+                                        show: previousUpdateLevel;
+                                        show: ' -> ' ;
+                                        show: SystemVersion current highestUpdate;
+                                        show: ' =========='; cr ]
+                        ifFalse: [
+                                interactive
+                                        ifTrue: [ self inform: 'Update completed.' ].
+                                Transcript cr; show: '==========  Update completed. ==========' translated; cr ] ].!

Item was added:
+ ----- Method: MCMcmUpdater>>basicUpdateFromRepositories:do: (in category 'updating') -----
+ basicUpdateFromRepositories: repositoryUrls do: repositoryBlock
+
+        | repos config |
+        MCConfiguration upgradeIsMerge: true.
+        "The list of repositories to consult in order"
+        repos := repositoryUrls collect: [:url| 
+                MCRepositoryGroup default repositories
+                        detect: [:repo | repo description = url]
+                        ifNone: [ | repo |
+                                repo := MCHttpRepository location: url.
+                                MCRepositoryGroup default addRepository: repo.
+                                repo]].
+       
+        "The list of updates-author.version.mcm sorted by version"
+        repos do: [:repo |
+                config := repositoryBlock value: repo].
+        ^ config!

Item was added:
+ ----- Method: MCMcmUpdater>>basicUpdateFromRepository:thenDo: (in category 'updating') -----
+ basicUpdateFromRepository: aRepository thenDo: configBlock
+
+        ^ self
+                basicUpdateFromRepository: aRepository
+                whileUpdatesSatisfy: [true]
+                upToUpdate: nil
+                thenDo: configBlock!

Item was added:
+ ----- Method: MCMcmUpdater>>basicUpdateFromRepository:whileUpdatesSatisfy:upToUpdate:thenDo: (in category 'updating') -----
+ basicUpdateFromRepository: aRepository whileUpdatesSatisfy: updateBlock upToUpdate: anIntegerOrNil thenDo: configBlock
+
+        | config |
+        aRepository cacheAllFileNamesDuring: [ | updateList |
+                updateList := self updateListFor: aRepository.
+                "Proceed only if there are updates available at all."
+                updateList ifNotEmpty: [
+                        updateList := self refreshUpdateMapFor: aRepository with: updateList.
+                        "Now process each update file. Check if we have all dependencies and if not, load the entire configuration (this is mostly to skip older updates quickly)"
+                        [:exit | updateList
+                                do: [:assoc |
+                                        (updateBlock cull: assoc key cull: assoc value) ifFalse: exit.
+                                        ProgressNotification signal: '' extra: ('Processing {1}' translated format: {assoc value}).
+                                        config := aRepository versionNamed: assoc value.
+                                        anIntegerOrNil ifNotNil: [
+                                                config currentSystemVersion highestUpdate <= anIntegerOrNil ifFalse: exit].
+                                        self updateFromConfig: config upToUpdate: anIntegerOrNil.
+                                        anIntegerOrNil ifNotNil: [
+                                                config currentSystemVersion highestUpdate < config totalSystemVersion highestUpdate ifTrue: exit].
+                                        self lastUpdateMap at: aRepository description put: assoc key]
+                                displayingProgress: 'Processing configurations' translated] valueWithExit.
+                        configBlock cull: config]].
+        ^ config!

Item was changed:
  ----- Method: MCMcmUpdater>>doUpdate: (in category 'updating') -----
  doUpdate: interactive
+        "Update the image by loading all pending updates from the server."
-        "Update the image by loading all pending updates from the server. If this is
-        the default updater for the system, update the system version when complete.
-        If interteractive use a modal notifier, otherwise only update the transcript.
-        Flush all caches. If a previous download failed this is often helpful"
 
+        ^ self basicDoUpdate: interactive do: [
+                self updateFromRepository ]!
-        | config previousUpdateLevel ensureTranscriptSetting |
-        previousUpdateLevel := SystemVersion current highestUpdate.
-        MCFileBasedRepository flushAllCaches.  
-        ensureTranscriptSetting := MCConfiguration ensureOpenTranscript.
-        [ MCConfiguration ensureOpenTranscript: interactive.
-        config := self updateFromRepository.
-        config ifNil: [
-                interactive ifTrue: [ ^self inform: 'Unable to retrieve updates from remote repository.' translated ].
-                Transcript cr; show: '==========  Unable to retrieve updates from remote repository. ==========' translated; cr.
-                ^ self ].
-        MCMcmUpdater default == self
-                ifTrue: [
-                        config setSystemVersion.
-                        interactive ifTrue: [
-                                self inform: (self updateMessageFor: previousUpdateLevel)].
-                        Transcript cr;
-                                show: '==========  Update completed:  ' translated;
-                                show: previousUpdateLevel;
-                                show: ' -> ' ;
-                                show: SystemVersion current highestUpdate;
-                                show: ' =========='; cr ]
-                ifFalse: [
-                        interactive
-                                ifTrue: [ self inform: 'Update completed.' ].
-                        Transcript cr; show: '==========  Update completed. ==========' translated; cr ] ]
-                ensure: [ MCConfiguration ensureOpenTranscript: ensureTranscriptSetting].
-
-        !

Item was changed:
  ----- Method: MCMcmUpdater>>doUpdate:upTo: (in category 'updating') -----
  doUpdate: interactive upTo: versionNumber
+        "Update the image by loading all pending updates from the server until update stream with the specified version number has been processed."
-        "Update the image by loading all pending updates from the server. If this is
-        the default updater for the system, update the system version when complete.
-        If interteractive use a modal notifier, otherwise only update the transcript.
-        Flush all caches. If a previous download failed this is often helpful"
 
+        ^ self basicDoUpdate: interactive do: [
+                self updateFromRepositories: {self repository} upTo: versionNumber ]!
-        | config previousUpdateLevel |
-        previousUpdateLevel := SystemVersion current highestUpdate.
-        MCFileBasedRepository flushAllCaches.
-        config := self updateFromRepositories: { self repository } upTo: versionNumber.
-        config ifNil: [
-                interactive ifTrue: [ ^self inform: 'Unable to retrieve updates from remote repository.' translated ].
-                Transcript cr; show: '==========  Unable to retrieve updates from remote repository. ==========' translated; cr.
-                ^ self ].
-        MCMcmUpdater default == self
-                ifTrue: [
-                        config setSystemVersion.
-                        interactive ifTrue: [
-                                self inform: (self updateMessageFor: previousUpdateLevel)].
-                        Transcript cr;
-                                show: '==========  Update completed:  ' translated;
-                                show: previousUpdateLevel;
-                                show: ' -> ' ;
-                                show: SystemVersion current highestUpdate;
-                                show: ' =========='; cr ]
-                ifFalse: [
-                        interactive
-                                ifTrue: [ self inform: 'Update completed.' ].
-                        Transcript cr; show: '==========  Update completed. ==========' translated; cr ]
-        !

Item was added:
+ ----- Method: MCMcmUpdater>>doUpdate:upToUpdate: (in category 'updating') -----
+ doUpdate: interactive upToUpdate: anIntegerOrNil
+        "Update the image by loading all pending updates from the server until the system version has reached at least anInteger. If anInteger is nil, install all available updates."
+
+        ^ self basicDoUpdate: interactive do: [
+                self updateFromRepositories: {self repository} upToUpdate: anIntegerOrNil ]!

Item was changed:
  ----- Method: MCMcmUpdater>>doUpdateUpTo: (in category 'updating') -----
  doUpdateUpTo: versionNumber
+        "Update the image by loading all pending updates from the server until update stream with the specified version number has been processed."
-        "Update the image by loading all pending updates from the server. If this is
-        the default updater for the system, update the system version when complete.
-        Flush all caches. If a previous download failed this is often helpful"
 
         ^self doUpdate: true upTo: versionNumber
  !

Item was changed:
  ----- Method: MCMcmUpdater>>updateFromConfig: (in category 'updating') -----
  updateFromConfig: config
 
+        ^ self updateFromConfig: config upToUpdate: nil!
-        "Skip packages that were specifically unloaded"
-        config dependencies: (config dependencies
-                reject: [:dep| self class skipPackages includes: dep package name]).
-        self class updateMissingPackages ifFalse:[
-                "Skip packages that are not in the image"
-                config dependencies: (config dependencies
-                        select: [:dep| dep package hasWorkingCopy])].
-        (config dependencies allSatisfy:[:dep| dep isFulfilled])
-                ifFalse:[config upgrade].
- !

Item was added:
+ ----- Method: MCMcmUpdater>>updateFromConfig:upToUpdate: (in category 'updating') -----
+ updateFromConfig: config upToUpdate: anIntegerOrNil
+
+        "Skip packages that were specifically unloaded"
+        config dependencies: (config dependencies
+                reject: [:dep | self class skipPackages includes: dep package name]).
+        self class updateMissingPackages ifFalse: [
+                "Skip packages that are not in the image"
+                config dependencies: (config dependencies
+                        select: [:dep | dep package hasWorkingCopy])].
+        (config dependencies allSatisfy: [:dep | dep isFulfilled])
+                ifFalse: [config upgradeUpToUpdate: anIntegerOrNil].!

Item was changed:
  ----- Method: MCMcmUpdater>>updateFromRepositories:upTo: (in category 'updating') -----
  updateFromRepositories: repositoryUrls upTo: versionNumber
+        "
+        MCMcmUpdater default updateFromRepositories: #('http://squeaksource.com/MCUpdateTest') upTo: 3
+        "
-        "MCMcmUpdater updateFromRepositories: #(
-                'http://squeaksource.com/MCUpdateTest'
-        )"
 
+        ^ self basicUpdateFromRepositories: repositoryUrls do: [:repo |
+                self updateFromRepository: repo upTo: versionNumber]!
-        | repos config |
-        MCConfiguration upgradeIsMerge: true.
-        "The list of repositories to consult in order"
-        repos := repositoryUrls collect:[:url|
-                MCRepositoryGroup default repositories
-                        detect:[:r| r description = url]
-                        ifNone:[ | r |
-                                r := MCHttpRepository location: url user: '' password: ''.
-                                MCRepositoryGroup default addRepository: r.
-                                r]].
-
-        "The list of updates-author.version.mcm sorted by version"
-        repos do:[ :r | config := self updateFromRepository: r upTo: versionNumber].
-        ^config!

Item was added:
+ ----- Method: MCMcmUpdater>>updateFromRepositories:upToUpdate: (in category 'updating') -----
+ updateFromRepositories: repositoryUrls upToUpdate: anIntegerOrNil
+        "
+        MCMcmUpdater default updateFromRepositories: #('http://squeaksource.com/MCUpdateTest') upToUpdate: 2130
+        "
+
+        ^ self basicUpdateFromRepositories: repositoryUrls do: [:repo |
+                self updateFromRepository: repo upToUpdate: anIntegerOrNil ]!

Item was changed:
  ----- Method: MCMcmUpdater>>updateFromRepository (in category 'updating') -----
  updateFromRepository
 
+        ^ self updateFromRepository: self getRepositoryFromRepositoryGroup!
-        | config repo |
-        repo := self getRepositoryFromRepositoryGroup.
-        repo cacheAllFileNamesDuring: [ | updateList |
-                updateList := self updateListFor: repo.
-                "Proceed only if there are updates available at all."
-                updateList ifNotEmpty: [
-                        updateList := self refreshUpdateMapFor: repo with: updateList.
-                        "Now process each update file. Check if we have all dependencies and if not,
-                        load the entire configuration (this is mostly to skip older updates quickly)"
-                        updateList do:[:assoc|
-                                ProgressNotification signal: '' extra: 'Processing ', assoc value.
-                                config := repo versionNamed: assoc value.
-                                self updateFromConfig: config.
-                                self lastUpdateMap at: repo description put: assoc key.
-                        ] displayingProgress: 'Processing configurations'.
-                        "We've loaded all the provided update configurations.
-                        Use the latest configuration to update all the remaining packages."
-                        (self useLatestPackagesFrom: repo) ifTrue: [
-                                config updateFromRepositories.
-                                config upgrade].
-                ]].
-        ^ config
- !

Item was added:
+ ----- Method: MCMcmUpdater>>updateFromRepository: (in category 'updating') -----
+ updateFromRepository: aRepository
+
+        ^ self basicUpdateFromRepository: aRepository thenDo: [:config |
+                "We've loaded all the provided update configurations. Use the latest configuration to update all the remaining packages."
+                (self useLatestPackagesFrom: aRepository) ifTrue: [
+                        config updateFromRepositories.
+                        config upgrade]]!

Item was changed:
  ----- Method: MCMcmUpdater>>updateFromRepository:upTo: (in category 'updating') -----
+ updateFromRepository: aRepository upTo: versionNumber
- updateFromRepository: repository upTo: versionNumber
 
+        ^ self
+                basicUpdateFromRepository: aRepository
+                whileUpdatesSatisfy: [:updateVersion | updateVersion <= versionNumber]
+                upToUpdate: nil
+                thenDo: []!
-        | config |
-        repository cacheAllFileNamesDuring: [ | updateList |
-                updateList := self updateListFor: repository.
-                "Proceed only if there are updates available at all."
-                updateList ifNotEmpty: [
-                        updateList := self refreshUpdateMapFor: repository with: updateList.
-                        "Now process each update file. Check if we have all dependencies and if not,
-                        load the entire configuration (this is mostly to skip older updates quickly)"
-                        updateList do:[:assoc|
-                                assoc key > versionNumber ifTrue: [^config].
-                                ProgressNotification signal: '' extra: 'Processing ', assoc value.
-                                config := repository versionNamed: assoc value.
-                                self updateFromConfig: config.
-                                self lastUpdateMap at: repository description put: assoc key.
-                        ] displayingProgress: 'Processing configurations'.
-                ]].
-        ^config
- !

Item was added:
+ ----- Method: MCMcmUpdater>>updateFromRepository:upToUpdate: (in category 'updating') -----
+ updateFromRepository: aRepository upToUpdate: anIntegerOrNil
+
+        ^ self
+                basicUpdateFromRepository: aRepository
+                whileUpdatesSatisfy: [true]
+                upToUpdate: anIntegerOrNil
+                thenDo: []!





Reply | Threaded
Open this post in threaded view
|

Re: The Inbox: MonticelloConfigurations-ct.166.mcz

codefrau
“Interpolating” would be unreliable IMHO. There is a direct correspondence from explicit MCMs to version numbers, but not three other way around. You could say “please build version 20313” which would succeed because there is a config that produces that number, but if you were asking for version 20314 then we wouldn’t know which package to update beyond the version given in the config. 

– Vanessa –

On Thu, Apr 15, 2021 at 04:08 Marcel Taeumel <[hidden email]> wrote:
Hi Christoph,

as Vanessa explained, updates up to a specific MCM in the current update map might work. This also would not apply to cross-release/trunk updates in general. But might work most of the time since we rarely update release repositories.

Since we now also have comments in MCMs, a user might actually be able to pick the desired MCM from a list of not-yet-loaded ones.

(Hmm... maybe approximating a list of versions from interpolating the build version between two MCMs might work ...)

Best,
Marcel

Am 15.04.2021 02:13:34 schrieb Vanessa Freudenberg <[hidden email]>:

Hi Christoph,

Good idea, but is not quite that simple. The image "version number" does not uniquely identify a specific set of package versions. It's not enough to build a similar image again.

To have somewhat reproducible builds (they're not bit-identical) you need to store the actual list of loaded package versions. The easiest way to do that would be using an MCM. Only when an explicit config map is deployed that indeed is a "fixed point" with a defined set of packages.

MCMcmUpdater normally loads each explicit update map, plus then automatically updates the last map to the latest package versions. That is governed by the useLatestPackagesFrom: method.

The latest config map appears to be

A method for reproducible builds could take e.g. 'mt.485' as an argument and should produce an image with version number 20313:

((MCRepository trunk versionNamed: 'update-mt.485.mcm') dependencies collect: [:dep | dep versionInfo versionNumber]) sum


–Vanessa–


On Thu, Apr 8, 2021 at 1:37 PM Thiede, Christoph <[hidden email]> wrote:

Please review! :-)


A concrete use case I was having in mind for this feature is to allow arbitrary Squeak Trunk version numbers in smalltalkCI, see: https://github.com/hpi-swa/smalltalkCI/pull/506

At the moment, it is WIP - an unpolished prototype only, but the idea is that you could specify something like Squeak64-18242 instead of Squeak64-trunk in the CI/CLI args.


Nevertheless, even if the proposal for SCI should get rejected, I think this is a helpful feature because releases are quite rare in Squeak and I find it important to tag certain states in the Trunk evolution.

To improve support for the SCI PR, I would also like to backport these changes to 5.3 - there was only a single merge conflict, so if you agree with this proposal, I can send you a changeset for 5.3. Thanks in advance! :-)

Best,
Christoph

Von: Squeak-dev <[hidden email]> im Auftrag von [hidden email] <[hidden email]>
Gesendet: Donnerstag, 8. April 2021 22:30:32
An: [hidden email]
Betreff: [squeak-dev] The Inbox: MonticelloConfigurations-ct.166.mcz
 
A new version of MonticelloConfigurations was added to project The Inbox:
http://source.squeak.org/inbox/MonticelloConfigurations-ct.166.mcz

==================== Summary ====================

Name: MonticelloConfigurations-ct.166
Author: ct
Time: 8 April 2021, 10:30:30.702818 pm
UUID: 6d887a86-fe0f-8e4c-a878-e40e8e612918
Ancestors: MonticelloConfigurations-mt.165

Adds support in MCMcmUpdater to install updates up to a specified system version number. This can be helpful when you want to bring an image into an exact state of an alpha version. I.e., a bug report or a tool can refer to Squeak6.0 Alpha #19570. Now it's possible to bring your 5.3 image into this state by evaluating:

        MCMcmUpdater default doUpdate: true upToUpdate: 19570.

Still, it is possible to install all updates or navigate forward to a certain upstream version using the existing protocol:

        MCMcmUpdater default doUpdate: true.
        MCMcmUpdater default doUpdate: true upTo: 470.

Detailed changelog (anticlimax):

- Added 'upToUpdate:' variant of update selectors on MCMcmUpdater.
- Refactored existing update selectors to deduplicate the logic for doing a complete update (#doUpdate: etc.) versus installing all updates up to an upstream version (#doUpdate:upTo: etc.).
- Added #currentSystemVersion and #totalSystemVersion on MCConfiguration to determine the current resp. eventual system version as implied by the update stream.
- Renamed (+ deprecated) #depsSatisfying:version(s)Do:displayingProgress: due to misleading use of singular in the block keyword.
- Improved multilingual support for the updater.
- Improved documentation of the update protocol.

Note that in some situations, a small number of follow-up updates will be installed that go beyond the specified system version number. This is caused by interdependencies between the target version and its postdecessors when a definiton was moved from one package into another one. See sender of MCReorganizationPreloader.

=============== Diff against MonticelloConfigurations-mt.165 ===============

Item was added:
+ ----- Method: MCConfiguration class>>ensureOpenTranscript:during: (in category 'preferences') -----
+ ensureOpenTranscript: aBoolean during: aBlock
+
+        | previous |
+        previous := self ensureOpenTranscript.
+        self ensureOpenTranscript: aBoolean.
+        ^ aBlock ensure: [
+                self ensureOpenTranscript: previous]!

Item was added:
+ ----- Method: MCConfiguration>>currentSystemVersion (in category 'updating') -----
+ currentSystemVersion
+        "Answer the current system version found in my configuration (or the associated working copy)."
+
+        ^ self systemVersionForDeps: (self dependencies
+                collect: [:dep |
+                        dep isFulfilledByAncestors
+                                ifTrue: [dep versionInfo]
+                                ifFalse: [dep package workingCopy ancestors] ] )!

Item was added:
+ ----- Method: MCConfiguration>>depsSatisfying:upToUpdate:versionsDo:displayingProgress: (in category 'private') -----
+ depsSatisfying: selectBlock upToUpdate: anIntegerOrNil versionsDo: versionBlock displayingProgress: progressString
+
+        | count selectedVersions cleanWorkingCopies |
+        self cacheAllFileNamesDuring: [
+                self repositories do: [ :eachRepository |
+                        MCRepositoryGroup default addRepository: eachRepository ].
+               
+                "First, download selected versions"
+                count := 0.
+                selectedVersions := OrderedCollection new.
+                self withProgress: progressString in: self dependencies do: [ :dep | | verName repo |
+                        verName := dep versionInfo name.
+                        self class extraProgressInfo ifTrue:
+                                [ ProgressNotification signal: '' extra: ('Downloading {1}' translated format: {verName}) ].
+                        repo := self repositories
+                                detect: [ :eachRepository | eachRepository includesVersionNamed: verName ]
+                                ifNone:  [ self logError: ('Version {1} not found in any repository' translated format: {verName}).
+                                        self logError: 'Aborting' translated.
+                                        ^ count ].
+                        (selectBlock value: dep) ifTrue: [ | version |
+                                version := self versionNamed: verName for: dep from: repo.
+                                version ifNil: [ self logError: ('Could not download version {1} from {2}' translated format: {verName. repo description}).
+                                        self logError: 'Aborting' translated.
+                                        ^ count ].
+                                dep package workingCopy newRepositoryGroupIfDefault. "fix old working copies"
+                                dep package workingCopy repositoryGroup addRepository: repo.
+                                selectedVersions add: version ] ].
+               
+                "Then, process only those definitions that moved from one package to another, to avoid order dependence"
+                cleanWorkingCopies := MCWorkingCopy allManagers select:
+                        [ :wc | wc modified not and:
+                                [ selectedVersions anySatisfy: [ :v | wc package = v package ] ] ].
+                MCReorganizationPreloader preloadMovesBetween: selectedVersions.
+               
+                "Finally, load/merge selected versions"
+                [:exit | self withProgress: progressString in: selectedVersions do: [ :version |
+                        self logUpdate: version package with: version.
+                        self class extraProgressInfo ifTrue:
+                                [ ProgressNotification signal: '' extra: ('Installing {1}' translated format: {version info name}) ].
+                        versionBlock value: version.
+                        count := count + 1.
+                        anIntegerOrNil ifNotNil: [
+                                self currentSystemVersion highestUpdate < anIntegerOrNil ifFalse: exit.
+                                self flag: #optimization. "ct: By customizing the system version computation, we could save redundant version downloads above. However, because of the preloading stage, this might become a bit more sophisticated." ] ].
+                ] valueWithExit.
+               
+                "Clean up packages made dirty by MCReorganizationPreloader"
+                cleanWorkingCopies
+                        select: [ :wc | wc modified ]
+                        thenDo: [ :wc | wc checkModified ].
+        ].
+        ^ count!

Item was changed:
  ----- Method: MCConfiguration>>depsSatisfying:versionDo:displayingProgress: (in category 'private') -----
+ depsSatisfying: selectBlock versionDo: versionBlock displayingProgress: progressString
+
+        self deprecated.
+        ^ self
+                depsSatisfying: selectBlock
+                versionsDo: versionBlock
+                displayingProgress: progressString!
- depsSatisfying: selectBlock versionDo: verBlock displayingProgress: progressString
-        | count selectedVersions cleanWorkingCopies |
-        self cacheAllFileNamesDuring: [
-                self repositories do: [ :eachRepository |
-                        MCRepositoryGroup default addRepository: eachRepository ].
-                "First, download selected versions"
-                count := 0.
-                selectedVersions := OrderedCollection new.
-                self withProgress: progressString in: self dependencies do: [ :dep | | verName repo |
-                        verName := dep versionInfo name.
-                        self class extraProgressInfo ifTrue:
-                                [ ProgressNotification signal: '' extra: 'Downloading ' , verName ].
-                        repo := self repositories
-                                detect: [ :eachRepository | eachRepository includesVersionNamed: verName ]
-                                ifNone:  [ self logError: 'Version ' , verName , ' not found in any repository'.
-                                        self logError: 'Aborting'.
-                                        ^ count ].
-                        (selectBlock value: dep) ifTrue: [ | version |
-                                version := self versionNamed: verName for: dep from: repo.
-                                version ifNil: [ self logError: 'Could not download version ' , verName , ' from ' , repo description.
-                                        self logError: 'Aborting'.
-                                        ^ count ].
-                                dep package workingCopy newRepositoryGroupIfDefault. "fix old working copies"
-                                dep package workingCopy repositoryGroup addRepository: repo.
-                                selectedVersions add: version]].
-                "Then, process only those definitions that moved from one package to another, to avoid order dependence"
-                cleanWorkingCopies := MCWorkingCopy allManagers select:
-                        [ :wc | wc modified not and:
-                                [ selectedVersions anySatisfy: [ :v | wc package = v package ] ] ].
-                MCReorganizationPreloader preloadMovesBetween: selectedVersions.
-                "Finally, load/merge selected versions"
-                self withProgress: progressString in: selectedVersions do: [ :version |
-                        self logUpdate: version package with: version.
-                        self class extraProgressInfo ifTrue:
-                                [ ProgressNotification signal: '' extra: 'Installing ' , version info name ].
-                        verBlock value: version.
-                        count := count + 1 ].
-                "Clean up packages made dirty by MCReorganizationPreloader"
-                cleanWorkingCopies
-                        select: [ :wc | wc modified ]
-                        thenDo: [ :wc | wc checkModified ].
-        ].
-        ^ count!

Item was added:
+ ----- Method: MCConfiguration>>depsSatisfying:versionsDo:displayingProgress: (in category 'private') -----
+ depsSatisfying: selectBlock versionsDo: versionBlock displayingProgress: progressString
+
+        ^ self
+                depsSatisfying: selectBlock
+                upToUpdate: nil
+                versionsDo: versionBlock
+                displayingProgress: progressString!

Item was changed:
  ----- Method: MCConfiguration>>load (in category 'actions') -----
  load
         ^self depsSatisfying: [:dep | dep isCurrent not]
+                versionsDo: [:ver | ver load]
+                displayingProgress: 'loading packages' translated!
-                versionDo: [:ver | ver load]
-                displayingProgress: 'loading packages'
- !

Item was changed:
  ----- Method: MCConfiguration>>merge (in category 'actions') -----
  merge
         ^self depsSatisfying: [:dep | dep isFulfilledByAncestors not]
+                versionsDo: [:ver | ver merge]
+                displayingProgress: 'merging packages' translated!
-                versionDo: [:ver | ver merge]
-                displayingProgress: 'merging packages'
- !

Item was changed:
  ----- Method: MCConfiguration>>setSystemVersion (in category 'updating') -----
  setSystemVersion
-        "Set the current system version date to the latest date found in my configuration (or the associated working copy). Also set the highest update number to the sum of version numbers in my configuration."
 
+        | newVersion |
+        newVersion := self currentSystemVersion.
-        | versionNumbers versionDates |
-        versionNumbers := self dependencies collect: [:d |
-                (d versionInfo name copyAfterLast: $.) asInteger].
-        versionDates := self dependencies collect: [:d |
-                d versionInfo date
-                        ifNil: [d package workingCopy ancestors first date]].
         SystemVersion current
+                date: newVersion date;
+                highestUpdate: newVersion highestUpdate.!
-                date: versionDates max;
-                highestUpdate: versionNumbers sum.!

Item was added:
+ ----- Method: MCConfiguration>>systemVersionForDeps: (in category 'updating') -----
+ systemVersionForDeps: dependencies
+        "Answer the system version that will be reached by loading the passed dependencies."
+
+        | versionNumbers versionDates |
+        versionNumbers := dependencies collect: [:dep |
+                dep isCollection
+                        ifFalse: [dep name versionNumber]
+                        ifTrue: [(dep collect: [:version |
+                                version name versionNumber])
+                                        ifEmpty: [0]
+                                        ifNotEmpty: #max]].
+        versionDates := dependencies with: self dependencies collect: [:dep :originalDep |
+                (dep isCollection ifFalse: [dep date])
+                        ifNil: [originalDep package workingCopy ancestors
+                                        ifEmpty: [nil]
+                                        ifNotEmpty: [:ancestors | ancestors first date]]].
+       
+        ^ SystemVersion new
+                date: ((versionDates copyWithout: nil)
+                        ifEmpty: [nil]
+                        ifNotEmpty: #max);
+                highestUpdate: versionNumbers sum!

Item was added:
+ ----- Method: MCConfiguration>>totalSystemVersion (in category 'updating') -----
+ totalSystemVersion
+        "Answer the total system version that will be reached by upgrading the receiver."
+
+        ^ self systemVersionForDeps: (self dependencies collect: #versionInfo)!

Item was changed:
  ----- Method: MCConfiguration>>upgrade (in category 'actions') -----
  upgrade
+
+        ^ self upgradeUpToUpdate: nil!
-        ^self depsSatisfying:
-                        [:dep | dep isFulfilledByAncestors not]
-                versionDo:
-                        [:ver |
-                        (self class upgradeIsMerge and: [ver shouldMerge])
-                                ifFalse: [ver load]
-                                ifTrue:
-                                        [[ver merge]
-                                                on: MCNoChangesException
-                                                do: [:req| req resume ]
-                                                on: MCMergeResolutionRequest
-                                                do: [:request |
-                                                        request merger conflicts isEmpty
-                                                                ifTrue: [request resume: true]
-                                                                ifFalse: [request pass]]
-                                                on: Deprecation
-                                                do: [:req| req resume ]]]
-                displayingProgress: 'upgrading packages'!

Item was added:
+ ----- Method: MCConfiguration>>upgradeUpToUpdate: (in category 'actions') -----
+ upgradeUpToUpdate: anIntegerOrNil
+
+        ^ self
+                depsSatisfying: [:dep |
+                        dep isFulfilledByAncestors not]
+                upToUpdate: anIntegerOrNil
+                versionsDo: [:ver |
+                        (self class upgradeIsMerge and: [ver shouldMerge])
+                                ifFalse: [ver load]
+                                ifTrue:
+                                        [[ver merge]
+                                                on: MCNoChangesException
+                                                do: [:req | req resume ]
+                                                on: MCMergeResolutionRequest
+                                                do: [:request |
+                                                        request merger conflicts isEmpty
+                                                                ifTrue: [request resume: true]
+                                                                ifFalse: [request pass]]
+                                                on: Deprecation
+                                                do: [:req | req resume ]]]
+                displayingProgress: 'upgrading packages' translated!

Item was added:
+ ----- Method: MCMcmUpdater>>basicDoUpdate:do: (in category 'updating') -----
+ basicDoUpdate: interactive do: aBlock
+        "Update the image by evaluating aBlock. If this is the default updater for the system, update the system version when complete. If interactive, use a modal notifier, otherwise only update the transcript. Flush all caches. If a previous download failed, this is often helpful."
+
+        | config previousUpdateLevel |
+        previousUpdateLevel := SystemVersion current highestUpdate.
+        MCFileBasedRepository flushAllCaches.
+       
+        MCConfiguration ensureOpenTranscript: interactive during: [
+                config := aBlock value.
+       
+                config ifNil: [
+                        interactive ifTrue: [^ self inform: 'Unable to retrieve updates from remote repository.' translated].
+                        Transcript cr; show: '==========  Unable to retrieve updates from remote repository. ==========' translated; cr.
+                        ^ self ].
+       
+                MCMcmUpdater default == self
+                        ifTrue: [
+                                config setSystemVersion.
+                                interactive ifTrue: [
+                                        self inform: (self updateMessageFor: previousUpdateLevel)].
+                                Transcript cr;
+                                        show: '==========  Update completed:  ' translated;
+                                        show: previousUpdateLevel;
+                                        show: ' -> ' ;
+                                        show: SystemVersion current highestUpdate;
+                                        show: ' =========='; cr ]
+                        ifFalse: [
+                                interactive
+                                        ifTrue: [ self inform: 'Update completed.' ].
+                                Transcript cr; show: '==========  Update completed. ==========' translated; cr ] ].!

Item was added:
+ ----- Method: MCMcmUpdater>>basicUpdateFromRepositories:do: (in category 'updating') -----
+ basicUpdateFromRepositories: repositoryUrls do: repositoryBlock
+
+        | repos config |
+        MCConfiguration upgradeIsMerge: true.
+        "The list of repositories to consult in order"
+        repos := repositoryUrls collect: [:url| 
+                MCRepositoryGroup default repositories
+                        detect: [:repo | repo description = url]
+                        ifNone: [ | repo |
+                                repo := MCHttpRepository location: url.
+                                MCRepositoryGroup default addRepository: repo.
+                                repo]].
+       
+        "The list of updates-author.version.mcm sorted by version"
+        repos do: [:repo |
+                config := repositoryBlock value: repo].
+        ^ config!

Item was added:
+ ----- Method: MCMcmUpdater>>basicUpdateFromRepository:thenDo: (in category 'updating') -----
+ basicUpdateFromRepository: aRepository thenDo: configBlock
+
+        ^ self
+                basicUpdateFromRepository: aRepository
+                whileUpdatesSatisfy: [true]
+                upToUpdate: nil
+                thenDo: configBlock!

Item was added:
+ ----- Method: MCMcmUpdater>>basicUpdateFromRepository:whileUpdatesSatisfy:upToUpdate:thenDo: (in category 'updating') -----
+ basicUpdateFromRepository: aRepository whileUpdatesSatisfy: updateBlock upToUpdate: anIntegerOrNil thenDo: configBlock
+
+        | config |
+        aRepository cacheAllFileNamesDuring: [ | updateList |
+                updateList := self updateListFor: aRepository.
+                "Proceed only if there are updates available at all."
+                updateList ifNotEmpty: [
+                        updateList := self refreshUpdateMapFor: aRepository with: updateList.
+                        "Now process each update file. Check if we have all dependencies and if not, load the entire configuration (this is mostly to skip older updates quickly)"
+                        [:exit | updateList
+                                do: [:assoc |
+                                        (updateBlock cull: assoc key cull: assoc value) ifFalse: exit.
+                                        ProgressNotification signal: '' extra: ('Processing {1}' translated format: {assoc value}).
+                                        config := aRepository versionNamed: assoc value.
+                                        anIntegerOrNil ifNotNil: [
+                                                config currentSystemVersion highestUpdate <= anIntegerOrNil ifFalse: exit].
+                                        self updateFromConfig: config upToUpdate: anIntegerOrNil.
+                                        anIntegerOrNil ifNotNil: [
+                                                config currentSystemVersion highestUpdate < config totalSystemVersion highestUpdate ifTrue: exit].
+                                        self lastUpdateMap at: aRepository description put: assoc key]
+                                displayingProgress: 'Processing configurations' translated] valueWithExit.
+                        configBlock cull: config]].
+        ^ config!

Item was changed:
  ----- Method: MCMcmUpdater>>doUpdate: (in category 'updating') -----
  doUpdate: interactive
+        "Update the image by loading all pending updates from the server."
-        "Update the image by loading all pending updates from the server. If this is
-        the default updater for the system, update the system version when complete.
-        If interteractive use a modal notifier, otherwise only update the transcript.
-        Flush all caches. If a previous download failed this is often helpful"
 
+        ^ self basicDoUpdate: interactive do: [
+                self updateFromRepository ]!
-        | config previousUpdateLevel ensureTranscriptSetting |
-        previousUpdateLevel := SystemVersion current highestUpdate.
-        MCFileBasedRepository flushAllCaches.  
-        ensureTranscriptSetting := MCConfiguration ensureOpenTranscript.
-        [ MCConfiguration ensureOpenTranscript: interactive.
-        config := self updateFromRepository.
-        config ifNil: [
-                interactive ifTrue: [ ^self inform: 'Unable to retrieve updates from remote repository.' translated ].
-                Transcript cr; show: '==========  Unable to retrieve updates from remote repository. ==========' translated; cr.
-                ^ self ].
-        MCMcmUpdater default == self
-                ifTrue: [
-                        config setSystemVersion.
-                        interactive ifTrue: [
-                                self inform: (self updateMessageFor: previousUpdateLevel)].
-                        Transcript cr;
-                                show: '==========  Update completed:  ' translated;
-                                show: previousUpdateLevel;
-                                show: ' -> ' ;
-                                show: SystemVersion current highestUpdate;
-                                show: ' =========='; cr ]
-                ifFalse: [
-                        interactive
-                                ifTrue: [ self inform: 'Update completed.' ].
-                        Transcript cr; show: '==========  Update completed. ==========' translated; cr ] ]
-                ensure: [ MCConfiguration ensureOpenTranscript: ensureTranscriptSetting].
-
-        !

Item was changed:
  ----- Method: MCMcmUpdater>>doUpdate:upTo: (in category 'updating') -----
  doUpdate: interactive upTo: versionNumber
+        "Update the image by loading all pending updates from the server until update stream with the specified version number has been processed."
-        "Update the image by loading all pending updates from the server. If this is
-        the default updater for the system, update the system version when complete.
-        If interteractive use a modal notifier, otherwise only update the transcript.
-        Flush all caches. If a previous download failed this is often helpful"
 
+        ^ self basicDoUpdate: interactive do: [
+                self updateFromRepositories: {self repository} upTo: versionNumber ]!
-        | config previousUpdateLevel |
-        previousUpdateLevel := SystemVersion current highestUpdate.
-        MCFileBasedRepository flushAllCaches.
-        config := self updateFromRepositories: { self repository } upTo: versionNumber.
-        config ifNil: [
-                interactive ifTrue: [ ^self inform: 'Unable to retrieve updates from remote repository.' translated ].
-                Transcript cr; show: '==========  Unable to retrieve updates from remote repository. ==========' translated; cr.
-                ^ self ].
-        MCMcmUpdater default == self
-                ifTrue: [
-                        config setSystemVersion.
-                        interactive ifTrue: [
-                                self inform: (self updateMessageFor: previousUpdateLevel)].
-                        Transcript cr;
-                                show: '==========  Update completed:  ' translated;
-                                show: previousUpdateLevel;
-                                show: ' -> ' ;
-                                show: SystemVersion current highestUpdate;
-                                show: ' =========='; cr ]
-                ifFalse: [
-                        interactive
-                                ifTrue: [ self inform: 'Update completed.' ].
-                        Transcript cr; show: '==========  Update completed. ==========' translated; cr ]
-        !

Item was added:
+ ----- Method: MCMcmUpdater>>doUpdate:upToUpdate: (in category 'updating') -----
+ doUpdate: interactive upToUpdate: anIntegerOrNil
+        "Update the image by loading all pending updates from the server until the system version has reached at least anInteger. If anInteger is nil, install all available updates."
+
+        ^ self basicDoUpdate: interactive do: [
+                self updateFromRepositories: {self repository} upToUpdate: anIntegerOrNil ]!

Item was changed:
  ----- Method: MCMcmUpdater>>doUpdateUpTo: (in category 'updating') -----
  doUpdateUpTo: versionNumber
+        "Update the image by loading all pending updates from the server until update stream with the specified version number has been processed."
-        "Update the image by loading all pending updates from the server. If this is
-        the default updater for the system, update the system version when complete.
-        Flush all caches. If a previous download failed this is often helpful"
 
         ^self doUpdate: true upTo: versionNumber
  !

Item was changed:
  ----- Method: MCMcmUpdater>>updateFromConfig: (in category 'updating') -----
  updateFromConfig: config
 
+        ^ self updateFromConfig: config upToUpdate: nil!
-        "Skip packages that were specifically unloaded"
-        config dependencies: (config dependencies
-                reject: [:dep| self class skipPackages includes: dep package name]).
-        self class updateMissingPackages ifFalse:[
-                "Skip packages that are not in the image"
-                config dependencies: (config dependencies
-                        select: [:dep| dep package hasWorkingCopy])].
-        (config dependencies allSatisfy:[:dep| dep isFulfilled])
-                ifFalse:[config upgrade].
- !

Item was added:
+ ----- Method: MCMcmUpdater>>updateFromConfig:upToUpdate: (in category 'updating') -----
+ updateFromConfig: config upToUpdate: anIntegerOrNil
+
+        "Skip packages that were specifically unloaded"
+        config dependencies: (config dependencies
+                reject: [:dep | self class skipPackages includes: dep package name]).
+        self class updateMissingPackages ifFalse: [
+                "Skip packages that are not in the image"
+                config dependencies: (config dependencies
+                        select: [:dep | dep package hasWorkingCopy])].
+        (config dependencies allSatisfy: [:dep | dep isFulfilled])
+                ifFalse: [config upgradeUpToUpdate: anIntegerOrNil].!

Item was changed:
  ----- Method: MCMcmUpdater>>updateFromRepositories:upTo: (in category 'updating') -----
  updateFromRepositories: repositoryUrls upTo: versionNumber
+        "
+        MCMcmUpdater default updateFromRepositories: #('http://squeaksource.com/MCUpdateTest') upTo: 3
+        "
-        "MCMcmUpdater updateFromRepositories: #(
-                'http://squeaksource.com/MCUpdateTest'
-        )"
 
+        ^ self basicUpdateFromRepositories: repositoryUrls do: [:repo |
+                self updateFromRepository: repo upTo: versionNumber]!
-        | repos config |
-        MCConfiguration upgradeIsMerge: true.
-        "The list of repositories to consult in order"
-        repos := repositoryUrls collect:[:url|
-                MCRepositoryGroup default repositories
-                        detect:[:r| r description = url]
-                        ifNone:[ | r |
-                                r := MCHttpRepository location: url user: '' password: ''.
-                                MCRepositoryGroup default addRepository: r.
-                                r]].
-
-        "The list of updates-author.version.mcm sorted by version"
-        repos do:[ :r | config := self updateFromRepository: r upTo: versionNumber].
-        ^config!

Item was added:
+ ----- Method: MCMcmUpdater>>updateFromRepositories:upToUpdate: (in category 'updating') -----
+ updateFromRepositories: repositoryUrls upToUpdate: anIntegerOrNil
+        "
+        MCMcmUpdater default updateFromRepositories: #('http://squeaksource.com/MCUpdateTest') upToUpdate: 2130
+        "
+
+        ^ self basicUpdateFromRepositories: repositoryUrls do: [:repo |
+                self updateFromRepository: repo upToUpdate: anIntegerOrNil ]!

Item was changed:
  ----- Method: MCMcmUpdater>>updateFromRepository (in category 'updating') -----
  updateFromRepository
 
+        ^ self updateFromRepository: self getRepositoryFromRepositoryGroup!
-        | config repo |
-        repo := self getRepositoryFromRepositoryGroup.
-        repo cacheAllFileNamesDuring: [ | updateList |
-                updateList := self updateListFor: repo.
-                "Proceed only if there are updates available at all."
-                updateList ifNotEmpty: [
-                        updateList := self refreshUpdateMapFor: repo with: updateList.
-                        "Now process each update file. Check if we have all dependencies and if not,
-                        load the entire configuration (this is mostly to skip older updates quickly)"
-                        updateList do:[:assoc|
-                                ProgressNotification signal: '' extra: 'Processing ', assoc value.
-                                config := repo versionNamed: assoc value.
-                                self updateFromConfig: config.
-                                self lastUpdateMap at: repo description put: assoc key.
-                        ] displayingProgress: 'Processing configurations'.
-                        "We've loaded all the provided update configurations.
-                        Use the latest configuration to update all the remaining packages."
-                        (self useLatestPackagesFrom: repo) ifTrue: [
-                                config updateFromRepositories.
-                                config upgrade].
-                ]].
-        ^ config
- !

Item was added:
+ ----- Method: MCMcmUpdater>>updateFromRepository: (in category 'updating') -----
+ updateFromRepository: aRepository
+
+        ^ self basicUpdateFromRepository: aRepository thenDo: [:config |
+                "We've loaded all the provided update configurations. Use the latest configuration to update all the remaining packages."
+                (self useLatestPackagesFrom: aRepository) ifTrue: [
+                        config updateFromRepositories.
+                        config upgrade]]!

Item was changed:
  ----- Method: MCMcmUpdater>>updateFromRepository:upTo: (in category 'updating') -----
+ updateFromRepository: aRepository upTo: versionNumber
- updateFromRepository: repository upTo: versionNumber
 
+        ^ self
+                basicUpdateFromRepository: aRepository
+                whileUpdatesSatisfy: [:updateVersion | updateVersion <= versionNumber]
+                upToUpdate: nil
+                thenDo: []!
-        | config |
-        repository cacheAllFileNamesDuring: [ | updateList |
-                updateList := self updateListFor: repository.
-                "Proceed only if there are updates available at all."
-                updateList ifNotEmpty: [
-                        updateList := self refreshUpdateMapFor: repository with: updateList.
-                        "Now process each update file. Check if we have all dependencies and if not,
-                        load the entire configuration (this is mostly to skip older updates quickly)"
-                        updateList do:[:assoc|
-                                assoc key > versionNumber ifTrue: [^config].
-                                ProgressNotification signal: '' extra: 'Processing ', assoc value.
-                                config := repository versionNamed: assoc value.
-                                self updateFromConfig: config.
-                                self lastUpdateMap at: repository description put: assoc key.
-                        ] displayingProgress: 'Processing configurations'.
-                ]].
-        ^config
- !

Item was added:
+ ----- Method: MCMcmUpdater>>updateFromRepository:upToUpdate: (in category 'updating') -----
+ updateFromRepository: aRepository upToUpdate: anIntegerOrNil
+
+        ^ self
+                basicUpdateFromRepository: aRepository
+                whileUpdatesSatisfy: [true]
+                upToUpdate: anIntegerOrNil
+                thenDo: []!






Reply | Threaded
Open this post in threaded view
|

Re: The Inbox: MonticelloConfigurations-ct.166.mcz

marcel.taeumel
Hmm... each package version has a timestamp. This means you can sort the versions created between two MCMs. From there you can compute an ordered list of potential build numbers. From that list, you might be able to pick one. :-) Still, unreliable and not worth the effort.

Best,
Marcel

Am 15.04.2021 18:25:18 schrieb Vanessa Freudenberg <[hidden email]>:

“Interpolating” would be unreliable IMHO. There is a direct correspondence from explicit MCMs to version numbers, but not three other way around. You could say “please build version 20313” which would succeed because there is a config that produces that number, but if you were asking for version 20314 then we wouldn’t know which package to update beyond the version given in the config. 

– Vanessa –

On Thu, Apr 15, 2021 at 04:08 Marcel Taeumel <[hidden email]> wrote:
Hi Christoph,

as Vanessa explained, updates up to a specific MCM in the current update map might work. This also would not apply to cross-release/trunk updates in general. But might work most of the time since we rarely update release repositories.

Since we now also have comments in MCMs, a user might actually be able to pick the desired MCM from a list of not-yet-loaded ones.

(Hmm... maybe approximating a list of versions from interpolating the build version between two MCMs might work ...)

Best,
Marcel

Am 15.04.2021 02:13:34 schrieb Vanessa Freudenberg <[hidden email]>:

Hi Christoph,

Good idea, but is not quite that simple. The image "version number" does not uniquely identify a specific set of package versions. It's not enough to build a similar image again.

To have somewhat reproducible builds (they're not bit-identical) you need to store the actual list of loaded package versions. The easiest way to do that would be using an MCM. Only when an explicit config map is deployed that indeed is a "fixed point" with a defined set of packages.

MCMcmUpdater normally loads each explicit update map, plus then automatically updates the last map to the latest package versions. That is governed by the useLatestPackagesFrom: method.

The latest config map appears to be

A method for reproducible builds could take e.g. 'mt.485' as an argument and should produce an image with version number 20313:

((MCRepository trunk versionNamed: 'update-mt.485.mcm') dependencies collect: [:dep | dep versionInfo versionNumber]) sum


–Vanessa–


On Thu, Apr 8, 2021 at 1:37 PM Thiede, Christoph <[hidden email]> wrote:

Please review! :-)


A concrete use case I was having in mind for this feature is to allow arbitrary Squeak Trunk version numbers in smalltalkCI, see: https://github.com/hpi-swa/smalltalkCI/pull/506

At the moment, it is WIP - an unpolished prototype only, but the idea is that you could specify something like Squeak64-18242 instead of Squeak64-trunk in the CI/CLI args.


Nevertheless, even if the proposal for SCI should get rejected, I think this is a helpful feature because releases are quite rare in Squeak and I find it important to tag certain states in the Trunk evolution.

To improve support for the SCI PR, I would also like to backport these changes to 5.3 - there was only a single merge conflict, so if you agree with this proposal, I can send you a changeset for 5.3. Thanks in advance! :-)

Best,
Christoph

Von: Squeak-dev <[hidden email]> im Auftrag von [hidden email] <[hidden email]>
Gesendet: Donnerstag, 8. April 2021 22:30:32
An: [hidden email]
Betreff: [squeak-dev] The Inbox: MonticelloConfigurations-ct.166.mcz
 
A new version of MonticelloConfigurations was added to project The Inbox:
http://source.squeak.org/inbox/MonticelloConfigurations-ct.166.mcz

==================== Summary ====================

Name: MonticelloConfigurations-ct.166
Author: ct
Time: 8 April 2021, 10:30:30.702818 pm
UUID: 6d887a86-fe0f-8e4c-a878-e40e8e612918
Ancestors: MonticelloConfigurations-mt.165

Adds support in MCMcmUpdater to install updates up to a specified system version number. This can be helpful when you want to bring an image into an exact state of an alpha version. I.e., a bug report or a tool can refer to Squeak6.0 Alpha #19570. Now it's possible to bring your 5.3 image into this state by evaluating:

        MCMcmUpdater default doUpdate: true upToUpdate: 19570.

Still, it is possible to install all updates or navigate forward to a certain upstream version using the existing protocol:

        MCMcmUpdater default doUpdate: true.
        MCMcmUpdater default doUpdate: true upTo: 470.

Detailed changelog (anticlimax):

- Added 'upToUpdate:' variant of update selectors on MCMcmUpdater.
- Refactored existing update selectors to deduplicate the logic for doing a complete update (#doUpdate: etc.) versus installing all updates up to an upstream version (#doUpdate:upTo: etc.).
- Added #currentSystemVersion and #totalSystemVersion on MCConfiguration to determine the current resp. eventual system version as implied by the update stream.
- Renamed (+ deprecated) #depsSatisfying:version(s)Do:displayingProgress: due to misleading use of singular in the block keyword.
- Improved multilingual support for the updater.
- Improved documentation of the update protocol.

Note that in some situations, a small number of follow-up updates will be installed that go beyond the specified system version number. This is caused by interdependencies between the target version and its postdecessors when a definiton was moved from one package into another one. See sender of MCReorganizationPreloader.

=============== Diff against MonticelloConfigurations-mt.165 ===============

Item was added:
+ ----- Method: MCConfiguration class>>ensureOpenTranscript:during: (in category 'preferences') -----
+ ensureOpenTranscript: aBoolean during: aBlock
+
+        | previous |
+        previous := self ensureOpenTranscript.
+        self ensureOpenTranscript: aBoolean.
+        ^ aBlock ensure: [
+                self ensureOpenTranscript: previous]!

Item was added:
+ ----- Method: MCConfiguration>>currentSystemVersion (in category 'updating') -----
+ currentSystemVersion
+        "Answer the current system version found in my configuration (or the associated working copy)."
+
+        ^ self systemVersionForDeps: (self dependencies
+                collect: [:dep |
+                        dep isFulfilledByAncestors
+                                ifTrue: [dep versionInfo]
+                                ifFalse: [dep package workingCopy ancestors] ] )!

Item was added:
+ ----- Method: MCConfiguration>>depsSatisfying:upToUpdate:versionsDo:displayingProgress: (in category 'private') -----
+ depsSatisfying: selectBlock upToUpdate: anIntegerOrNil versionsDo: versionBlock displayingProgress: progressString
+
+        | count selectedVersions cleanWorkingCopies |
+        self cacheAllFileNamesDuring: [
+                self repositories do: [ :eachRepository |
+                        MCRepositoryGroup default addRepository: eachRepository ].
+               
+                "First, download selected versions"
+                count := 0.
+                selectedVersions := OrderedCollection new.
+                self withProgress: progressString in: self dependencies do: [ :dep | | verName repo |
+                        verName := dep versionInfo name.
+                        self class extraProgressInfo ifTrue:
+                                [ ProgressNotification signal: '' extra: ('Downloading {1}' translated format: {verName}) ].
+                        repo := self repositories
+                                detect: [ :eachRepository | eachRepository includesVersionNamed: verName ]
+                                ifNone:  [ self logError: ('Version {1} not found in any repository' translated format: {verName}).
+                                        self logError: 'Aborting' translated.
+                                        ^ count ].
+                        (selectBlock value: dep) ifTrue: [ | version |
+                                version := self versionNamed: verName for: dep from: repo.
+                                version ifNil: [ self logError: ('Could not download version {1} from {2}' translated format: {verName. repo description}).
+                                        self logError: 'Aborting' translated.
+                                        ^ count ].
+                                dep package workingCopy newRepositoryGroupIfDefault. "fix old working copies"
+                                dep package workingCopy repositoryGroup addRepository: repo.
+                                selectedVersions add: version ] ].
+               
+                "Then, process only those definitions that moved from one package to another, to avoid order dependence"
+                cleanWorkingCopies := MCWorkingCopy allManagers select:
+                        [ :wc | wc modified not and:
+                                [ selectedVersions anySatisfy: [ :v | wc package = v package ] ] ].
+                MCReorganizationPreloader preloadMovesBetween: selectedVersions.
+               
+                "Finally, load/merge selected versions"
+                [:exit | self withProgress: progressString in: selectedVersions do: [ :version |
+                        self logUpdate: version package with: version.
+                        self class extraProgressInfo ifTrue:
+                                [ ProgressNotification signal: '' extra: ('Installing {1}' translated format: {version info name}) ].
+                        versionBlock value: version.
+                        count := count + 1.
+                        anIntegerOrNil ifNotNil: [
+                                self currentSystemVersion highestUpdate < anIntegerOrNil ifFalse: exit.
+                                self flag: #optimization. "ct: By customizing the system version computation, we could save redundant version downloads above. However, because of the preloading stage, this might become a bit more sophisticated." ] ].
+                ] valueWithExit.
+               
+                "Clean up packages made dirty by MCReorganizationPreloader"
+                cleanWorkingCopies
+                        select: [ :wc | wc modified ]
+                        thenDo: [ :wc | wc checkModified ].
+        ].
+        ^ count!

Item was changed:
  ----- Method: MCConfiguration>>depsSatisfying:versionDo:displayingProgress: (in category 'private') -----
+ depsSatisfying: selectBlock versionDo: versionBlock displayingProgress: progressString
+
+        self deprecated.
+        ^ self
+                depsSatisfying: selectBlock
+                versionsDo: versionBlock
+                displayingProgress: progressString!
- depsSatisfying: selectBlock versionDo: verBlock displayingProgress: progressString
-        | count selectedVersions cleanWorkingCopies |
-        self cacheAllFileNamesDuring: [
-                self repositories do: [ :eachRepository |
-                        MCRepositoryGroup default addRepository: eachRepository ].
-                "First, download selected versions"
-                count := 0.
-                selectedVersions := OrderedCollection new.
-                self withProgress: progressString in: self dependencies do: [ :dep | | verName repo |
-                        verName := dep versionInfo name.
-                        self class extraProgressInfo ifTrue:
-                                [ ProgressNotification signal: '' extra: 'Downloading ' , verName ].
-                        repo := self repositories
-                                detect: [ :eachRepository | eachRepository includesVersionNamed: verName ]
-                                ifNone:  [ self logError: 'Version ' , verName , ' not found in any repository'.
-                                        self logError: 'Aborting'.
-                                        ^ count ].
-                        (selectBlock value: dep) ifTrue: [ | version |
-                                version := self versionNamed: verName for: dep from: repo.
-                                version ifNil: [ self logError: 'Could not download version ' , verName , ' from ' , repo description.
-                                        self logError: 'Aborting'.
-                                        ^ count ].
-                                dep package workingCopy newRepositoryGroupIfDefault. "fix old working copies"
-                                dep package workingCopy repositoryGroup addRepository: repo.
-                                selectedVersions add: version]].
-                "Then, process only those definitions that moved from one package to another, to avoid order dependence"
-                cleanWorkingCopies := MCWorkingCopy allManagers select:
-                        [ :wc | wc modified not and:
-                                [ selectedVersions anySatisfy: [ :v | wc package = v package ] ] ].
-                MCReorganizationPreloader preloadMovesBetween: selectedVersions.
-                "Finally, load/merge selected versions"
-                self withProgress: progressString in: selectedVersions do: [ :version |
-                        self logUpdate: version package with: version.
-                        self class extraProgressInfo ifTrue:
-                                [ ProgressNotification signal: '' extra: 'Installing ' , version info name ].
-                        verBlock value: version.
-                        count := count + 1 ].
-                "Clean up packages made dirty by MCReorganizationPreloader"
-                cleanWorkingCopies
-                        select: [ :wc | wc modified ]
-                        thenDo: [ :wc | wc checkModified ].
-        ].
-        ^ count!

Item was added:
+ ----- Method: MCConfiguration>>depsSatisfying:versionsDo:displayingProgress: (in category 'private') -----
+ depsSatisfying: selectBlock versionsDo: versionBlock displayingProgress: progressString
+
+        ^ self
+                depsSatisfying: selectBlock
+                upToUpdate: nil
+                versionsDo: versionBlock
+                displayingProgress: progressString!

Item was changed:
  ----- Method: MCConfiguration>>load (in category 'actions') -----
  load
         ^self depsSatisfying: [:dep | dep isCurrent not]
+                versionsDo: [:ver | ver load]
+                displayingProgress: 'loading packages' translated!
-                versionDo: [:ver | ver load]
-                displayingProgress: 'loading packages'
- !

Item was changed:
  ----- Method: MCConfiguration>>merge (in category 'actions') -----
  merge
         ^self depsSatisfying: [:dep | dep isFulfilledByAncestors not]
+                versionsDo: [:ver | ver merge]
+                displayingProgress: 'merging packages' translated!
-                versionDo: [:ver | ver merge]
-                displayingProgress: 'merging packages'
- !

Item was changed:
  ----- Method: MCConfiguration>>setSystemVersion (in category 'updating') -----
  setSystemVersion
-        "Set the current system version date to the latest date found in my configuration (or the associated working copy). Also set the highest update number to the sum of version numbers in my configuration."
 
+        | newVersion |
+        newVersion := self currentSystemVersion.
-        | versionNumbers versionDates |
-        versionNumbers := self dependencies collect: [:d |
-                (d versionInfo name copyAfterLast: $.) asInteger].
-        versionDates := self dependencies collect: [:d |
-                d versionInfo date
-                        ifNil: [d package workingCopy ancestors first date]].
         SystemVersion current
+                date: newVersion date;
+                highestUpdate: newVersion highestUpdate.!
-                date: versionDates max;
-                highestUpdate: versionNumbers sum.!

Item was added:
+ ----- Method: MCConfiguration>>systemVersionForDeps: (in category 'updating') -----
+ systemVersionForDeps: dependencies
+        "Answer the system version that will be reached by loading the passed dependencies."
+
+        | versionNumbers versionDates |
+        versionNumbers := dependencies collect: [:dep |
+                dep isCollection
+                        ifFalse: [dep name versionNumber]
+                        ifTrue: [(dep collect: [:version |
+                                version name versionNumber])
+                                        ifEmpty: [0]
+                                        ifNotEmpty: #max]].
+        versionDates := dependencies with: self dependencies collect: [:dep :originalDep |
+                (dep isCollection ifFalse: [dep date])
+                        ifNil: [originalDep package workingCopy ancestors
+                                        ifEmpty: [nil]
+                                        ifNotEmpty: [:ancestors | ancestors first date]]].
+       
+        ^ SystemVersion new
+                date: ((versionDates copyWithout: nil)
+                        ifEmpty: [nil]
+                        ifNotEmpty: #max);
+                highestUpdate: versionNumbers sum!

Item was added:
+ ----- Method: MCConfiguration>>totalSystemVersion (in category 'updating') -----
+ totalSystemVersion
+        "Answer the total system version that will be reached by upgrading the receiver."
+
+        ^ self systemVersionForDeps: (self dependencies collect: #versionInfo)!

Item was changed:
  ----- Method: MCConfiguration>>upgrade (in category 'actions') -----
  upgrade
+
+        ^ self upgradeUpToUpdate: nil!
-        ^self depsSatisfying:
-                        [:dep | dep isFulfilledByAncestors not]
-                versionDo:
-                        [:ver |
-                        (self class upgradeIsMerge and: [ver shouldMerge])
-                                ifFalse: [ver load]
-                                ifTrue:
-                                        [[ver merge]
-                                                on: MCNoChangesException
-                                                do: [:req| req resume ]
-                                                on: MCMergeResolutionRequest
-                                                do: [:request |
-                                                        request merger conflicts isEmpty
-                                                                ifTrue: [request resume: true]
-                                                                ifFalse: [request pass]]
-                                                on: Deprecation
-                                                do: [:req| req resume ]]]
-                displayingProgress: 'upgrading packages'!

Item was added:
+ ----- Method: MCConfiguration>>upgradeUpToUpdate: (in category 'actions') -----
+ upgradeUpToUpdate: anIntegerOrNil
+
+        ^ self
+                depsSatisfying: [:dep |
+                        dep isFulfilledByAncestors not]
+                upToUpdate: anIntegerOrNil
+                versionsDo: [:ver |
+                        (self class upgradeIsMerge and: [ver shouldMerge])
+                                ifFalse: [ver load]
+                                ifTrue:
+                                        [[ver merge]
+                                                on: MCNoChangesException
+                                                do: [:req | req resume ]
+                                                on: MCMergeResolutionRequest
+                                                do: [:request |
+                                                        request merger conflicts isEmpty
+                                                                ifTrue: [request resume: true]
+                                                                ifFalse: [request pass]]
+                                                on: Deprecation
+                                                do: [:req | req resume ]]]
+                displayingProgress: 'upgrading packages' translated!

Item was added:
+ ----- Method: MCMcmUpdater>>basicDoUpdate:do: (in category 'updating') -----
+ basicDoUpdate: interactive do: aBlock
+        "Update the image by evaluating aBlock. If this is the default updater for the system, update the system version when complete. If interactive, use a modal notifier, otherwise only update the transcript. Flush all caches. If a previous download failed, this is often helpful."
+
+        | config previousUpdateLevel |
+        previousUpdateLevel := SystemVersion current highestUpdate.
+        MCFileBasedRepository flushAllCaches.
+       
+        MCConfiguration ensureOpenTranscript: interactive during: [
+                config := aBlock value.
+       
+                config ifNil: [
+                        interactive ifTrue: [^ self inform: 'Unable to retrieve updates from remote repository.' translated].
+                        Transcript cr; show: '==========  Unable to retrieve updates from remote repository. ==========' translated; cr.
+                        ^ self ].
+       
+                MCMcmUpdater default == self
+                        ifTrue: [
+                                config setSystemVersion.
+                                interactive ifTrue: [
+                                        self inform: (self updateMessageFor: previousUpdateLevel)].
+                                Transcript cr;
+                                        show: '==========  Update completed:  ' translated;
+                                        show: previousUpdateLevel;
+                                        show: ' -> ' ;
+                                        show: SystemVersion current highestUpdate;
+                                        show: ' =========='; cr ]
+                        ifFalse: [
+                                interactive
+                                        ifTrue: [ self inform: 'Update completed.' ].
+                                Transcript cr; show: '==========  Update completed. ==========' translated; cr ] ].!

Item was added:
+ ----- Method: MCMcmUpdater>>basicUpdateFromRepositories:do: (in category 'updating') -----
+ basicUpdateFromRepositories: repositoryUrls do: repositoryBlock
+
+        | repos config |
+        MCConfiguration upgradeIsMerge: true.
+        "The list of repositories to consult in order"
+        repos := repositoryUrls collect: [:url| 
+                MCRepositoryGroup default repositories
+                        detect: [:repo | repo description = url]
+                        ifNone: [ | repo |
+                                repo := MCHttpRepository location: url.
+                                MCRepositoryGroup default addRepository: repo.
+                                repo]].
+       
+        "The list of updates-author.version.mcm sorted by version"
+        repos do: [:repo |
+                config := repositoryBlock value: repo].
+        ^ config!

Item was added:
+ ----- Method: MCMcmUpdater>>basicUpdateFromRepository:thenDo: (in category 'updating') -----
+ basicUpdateFromRepository: aRepository thenDo: configBlock
+
+        ^ self
+                basicUpdateFromRepository: aRepository
+                whileUpdatesSatisfy: [true]
+                upToUpdate: nil
+                thenDo: configBlock!

Item was added:
+ ----- Method: MCMcmUpdater>>basicUpdateFromRepository:whileUpdatesSatisfy:upToUpdate:thenDo: (in category 'updating') -----
+ basicUpdateFromRepository: aRepository whileUpdatesSatisfy: updateBlock upToUpdate: anIntegerOrNil thenDo: configBlock
+
+        | config |
+        aRepository cacheAllFileNamesDuring: [ | updateList |
+                updateList := self updateListFor: aRepository.
+                "Proceed only if there are updates available at all."
+                updateList ifNotEmpty: [
+                        updateList := self refreshUpdateMapFor: aRepository with: updateList.
+                        "Now process each update file. Check if we have all dependencies and if not, load the entire configuration (this is mostly to skip older updates quickly)"
+                        [:exit | updateList
+                                do: [:assoc |
+                                        (updateBlock cull: assoc key cull: assoc value) ifFalse: exit.
+                                        ProgressNotification signal: '' extra: ('Processing {1}' translated format: {assoc value}).
+                                        config := aRepository versionNamed: assoc value.
+                                        anIntegerOrNil ifNotNil: [
+                                                config currentSystemVersion highestUpdate <= anIntegerOrNil ifFalse: exit].
+                                        self updateFromConfig: config upToUpdate: anIntegerOrNil.
+                                        anIntegerOrNil ifNotNil: [
+                                                config currentSystemVersion highestUpdate < config totalSystemVersion highestUpdate ifTrue: exit].
+                                        self lastUpdateMap at: aRepository description put: assoc key]
+                                displayingProgress: 'Processing configurations' translated] valueWithExit.
+                        configBlock cull: config]].
+        ^ config!

Item was changed:
  ----- Method: MCMcmUpdater>>doUpdate: (in category 'updating') -----
  doUpdate: interactive
+        "Update the image by loading all pending updates from the server."
-        "Update the image by loading all pending updates from the server. If this is
-        the default updater for the system, update the system version when complete.
-        If interteractive use a modal notifier, otherwise only update the transcript.
-        Flush all caches. If a previous download failed this is often helpful"
 
+        ^ self basicDoUpdate: interactive do: [
+                self updateFromRepository ]!
-        | config previousUpdateLevel ensureTranscriptSetting |
-        previousUpdateLevel := SystemVersion current highestUpdate.
-        MCFileBasedRepository flushAllCaches.  
-        ensureTranscriptSetting := MCConfiguration ensureOpenTranscript.
-        [ MCConfiguration ensureOpenTranscript: interactive.
-        config := self updateFromRepository.
-        config ifNil: [
-                interactive ifTrue: [ ^self inform: 'Unable to retrieve updates from remote repository.' translated ].
-                Transcript cr; show: '==========  Unable to retrieve updates from remote repository. ==========' translated; cr.
-                ^ self ].
-        MCMcmUpdater default == self
-                ifTrue: [
-                        config setSystemVersion.
-                        interactive ifTrue: [
-                                self inform: (self updateMessageFor: previousUpdateLevel)].
-                        Transcript cr;
-                                show: '==========  Update completed:  ' translated;
-                                show: previousUpdateLevel;
-                                show: ' -> ' ;
-                                show: SystemVersion current highestUpdate;
-                                show: ' =========='; cr ]
-                ifFalse: [
-                        interactive
-                                ifTrue: [ self inform: 'Update completed.' ].
-                        Transcript cr; show: '==========  Update completed. ==========' translated; cr ] ]
-                ensure: [ MCConfiguration ensureOpenTranscript: ensureTranscriptSetting].
-
-        !

Item was changed:
  ----- Method: MCMcmUpdater>>doUpdate:upTo: (in category 'updating') -----
  doUpdate: interactive upTo: versionNumber
+        "Update the image by loading all pending updates from the server until update stream with the specified version number has been processed."
-        "Update the image by loading all pending updates from the server. If this is
-        the default updater for the system, update the system version when complete.
-        If interteractive use a modal notifier, otherwise only update the transcript.
-        Flush all caches. If a previous download failed this is often helpful"
 
+        ^ self basicDoUpdate: interactive do: [
+                self updateFromRepositories: {self repository} upTo: versionNumber ]!
-        | config previousUpdateLevel |
-        previousUpdateLevel := SystemVersion current highestUpdate.
-        MCFileBasedRepository flushAllCaches.
-        config := self updateFromRepositories: { self repository } upTo: versionNumber.
-        config ifNil: [
-                interactive ifTrue: [ ^self inform: 'Unable to retrieve updates from remote repository.' translated ].
-                Transcript cr; show: '==========  Unable to retrieve updates from remote repository. ==========' translated; cr.
-                ^ self ].
-        MCMcmUpdater default == self
-                ifTrue: [
-                        config setSystemVersion.
-                        interactive ifTrue: [
-                                self inform: (self updateMessageFor: previousUpdateLevel)].
-                        Transcript cr;
-                                show: '==========  Update completed:  ' translated;
-                                show: previousUpdateLevel;
-                                show: ' -> ' ;
-                                show: SystemVersion current highestUpdate;
-                                show: ' =========='; cr ]
-                ifFalse: [
-                        interactive
-                                ifTrue: [ self inform: 'Update completed.' ].
-                        Transcript cr; show: '==========  Update completed. ==========' translated; cr ]
-        !

Item was added:
+ ----- Method: MCMcmUpdater>>doUpdate:upToUpdate: (in category 'updating') -----
+ doUpdate: interactive upToUpdate: anIntegerOrNil
+        "Update the image by loading all pending updates from the server until the system version has reached at least anInteger. If anInteger is nil, install all available updates."
+
+        ^ self basicDoUpdate: interactive do: [
+                self updateFromRepositories: {self repository} upToUpdate: anIntegerOrNil ]!

Item was changed:
  ----- Method: MCMcmUpdater>>doUpdateUpTo: (in category 'updating') -----
  doUpdateUpTo: versionNumber
+        "Update the image by loading all pending updates from the server until update stream with the specified version number has been processed."
-        "Update the image by loading all pending updates from the server. If this is
-        the default updater for the system, update the system version when complete.
-        Flush all caches. If a previous download failed this is often helpful"
 
         ^self doUpdate: true upTo: versionNumber
  !

Item was changed:
  ----- Method: MCMcmUpdater>>updateFromConfig: (in category 'updating') -----
  updateFromConfig: config
 
+        ^ self updateFromConfig: config upToUpdate: nil!
-        "Skip packages that were specifically unloaded"
-        config dependencies: (config dependencies
-                reject: [:dep| self class skipPackages includes: dep package name]).
-        self class updateMissingPackages ifFalse:[
-                "Skip packages that are not in the image"
-                config dependencies: (config dependencies
-                        select: [:dep| dep package hasWorkingCopy])].
-        (config dependencies allSatisfy:[:dep| dep isFulfilled])
-                ifFalse:[config upgrade].
- !

Item was added:
+ ----- Method: MCMcmUpdater>>updateFromConfig:upToUpdate: (in category 'updating') -----
+ updateFromConfig: config upToUpdate: anIntegerOrNil
+
+        "Skip packages that were specifically unloaded"
+        config dependencies: (config dependencies
+                reject: [:dep | self class skipPackages includes: dep package name]).
+        self class updateMissingPackages ifFalse: [
+                "Skip packages that are not in the image"
+                config dependencies: (config dependencies
+                        select: [:dep | dep package hasWorkingCopy])].
+        (config dependencies allSatisfy: [:dep | dep isFulfilled])
+                ifFalse: [config upgradeUpToUpdate: anIntegerOrNil].!

Item was changed:
  ----- Method: MCMcmUpdater>>updateFromRepositories:upTo: (in category 'updating') -----
  updateFromRepositories: repositoryUrls upTo: versionNumber
+        "
+        MCMcmUpdater default updateFromRepositories: #('http://squeaksource.com/MCUpdateTest') upTo: 3
+        "
-        "MCMcmUpdater updateFromRepositories: #(
-                'http://squeaksource.com/MCUpdateTest'
-        )"
 
+        ^ self basicUpdateFromRepositories: repositoryUrls do: [:repo |
+                self updateFromRepository: repo upTo: versionNumber]!
-        | repos config |
-        MCConfiguration upgradeIsMerge: true.
-        "The list of repositories to consult in order"
-        repos := repositoryUrls collect:[:url|
-                MCRepositoryGroup default repositories
-                        detect:[:r| r description = url]
-                        ifNone:[ | r |
-                                r := MCHttpRepository location: url user: '' password: ''.
-                                MCRepositoryGroup default addRepository: r.
-                                r]].
-
-        "The list of updates-author.version.mcm sorted by version"
-        repos do:[ :r | config := self updateFromRepository: r upTo: versionNumber].
-        ^config!

Item was added:
+ ----- Method: MCMcmUpdater>>updateFromRepositories:upToUpdate: (in category 'updating') -----
+ updateFromRepositories: repositoryUrls upToUpdate: anIntegerOrNil
+        "
+        MCMcmUpdater default updateFromRepositories: #('http://squeaksource.com/MCUpdateTest') upToUpdate: 2130
+        "
+
+        ^ self basicUpdateFromRepositories: repositoryUrls do: [:repo |
+                self updateFromRepository: repo upToUpdate: anIntegerOrNil ]!

Item was changed:
  ----- Method: MCMcmUpdater>>updateFromRepository (in category 'updating') -----
  updateFromRepository
 
+        ^ self updateFromRepository: self getRepositoryFromRepositoryGroup!
-        | config repo |
-        repo := self getRepositoryFromRepositoryGroup.
-        repo cacheAllFileNamesDuring: [ | updateList |
-                updateList := self updateListFor: repo.
-                "Proceed only if there are updates available at all."
-                updateList ifNotEmpty: [
-                        updateList := self refreshUpdateMapFor: repo with: updateList.
-                        "Now process each update file. Check if we have all dependencies and if not,
-                        load the entire configuration (this is mostly to skip older updates quickly)"
-                        updateList do:[:assoc|
-                                ProgressNotification signal: '' extra: 'Processing ', assoc value.
-                                config := repo versionNamed: assoc value.
-                                self updateFromConfig: config.
-                                self lastUpdateMap at: repo description put: assoc key.
-                        ] displayingProgress: 'Processing configurations'.
-                        "We've loaded all the provided update configurations.
-                        Use the latest configuration to update all the remaining packages."
-                        (self useLatestPackagesFrom: repo) ifTrue: [
-                                config updateFromRepositories.
-                                config upgrade].
-                ]].
-        ^ config
- !

Item was added:
+ ----- Method: MCMcmUpdater>>updateFromRepository: (in category 'updating') -----
+ updateFromRepository: aRepository
+
+        ^ self basicUpdateFromRepository: aRepository thenDo: [:config |
+                "We've loaded all the provided update configurations. Use the latest configuration to update all the remaining packages."
+                (self useLatestPackagesFrom: aRepository) ifTrue: [
+                        config updateFromRepositories.
+                        config upgrade]]!

Item was changed:
  ----- Method: MCMcmUpdater>>updateFromRepository:upTo: (in category 'updating') -----
+ updateFromRepository: aRepository upTo: versionNumber
- updateFromRepository: repository upTo: versionNumber
 
+        ^ self
+                basicUpdateFromRepository: aRepository
+                whileUpdatesSatisfy: [:updateVersion | updateVersion <= versionNumber]
+                upToUpdate: nil
+                thenDo: []!
-        | config |
-        repository cacheAllFileNamesDuring: [ | updateList |
-                updateList := self updateListFor: repository.
-                "Proceed only if there are updates available at all."
-                updateList ifNotEmpty: [
-                        updateList := self refreshUpdateMapFor: repository with: updateList.
-                        "Now process each update file. Check if we have all dependencies and if not,
-                        load the entire configuration (this is mostly to skip older updates quickly)"
-                        updateList do:[:assoc|
-                                assoc key > versionNumber ifTrue: [^config].
-                                ProgressNotification signal: '' extra: 'Processing ', assoc value.
-                                config := repository versionNamed: assoc value.
-                                self updateFromConfig: config.
-                                self lastUpdateMap at: repository description put: assoc key.
-                        ] displayingProgress: 'Processing configurations'.
-                ]].
-        ^config
- !

Item was added:
+ ----- Method: MCMcmUpdater>>updateFromRepository:upToUpdate: (in category 'updating') -----
+ updateFromRepository: aRepository upToUpdate: anIntegerOrNil
+
+        ^ self
+                basicUpdateFromRepository: aRepository
+                whileUpdatesSatisfy: [true]
+                upToUpdate: anIntegerOrNil
+                thenDo: []!






Reply | Threaded
Open this post in threaded view
|

Re: The Inbox: MonticelloConfigurations-ct.166.mcz

Nicolas Cellier
Yes, but it is the timestamp of initial commit, not the timestamp of
addition into trunk (unless you extract that by other means).
Since we can move versions from months ago from inbox (or anything
else) to trunk,
in presence of several branches, you can't really decide...

Le ven. 16 avr. 2021 à 10:09, Marcel Taeumel <[hidden email]> a écrit :

>
> Hmm... each package version has a timestamp. This means you can sort the versions created between two MCMs. From there you can compute an ordered list of potential build numbers. From that list, you might be able to pick one. :-) Still, unreliable and not worth the effort.
>
> Best,
> Marcel
>
> Am 15.04.2021 18:25:18 schrieb Vanessa Freudenberg <[hidden email]>:
>
> “Interpolating” would be unreliable IMHO. There is a direct correspondence from explicit MCMs to version numbers, but not three other way around. You could say “please build version 20313” which would succeed because there is a config that produces that number, but if you were asking for version 20314 then we wouldn’t know which package to update beyond the version given in the config.
>
> – Vanessa –
>
> On Thu, Apr 15, 2021 at 04:08 Marcel Taeumel <[hidden email]> wrote:
>>
>> Hi Christoph,
>>
>> as Vanessa explained, updates up to a specific MCM in the current update map might work. This also would not apply to cross-release/trunk updates in general. But might work most of the time since we rarely update release repositories.
>>
>> Since we now also have comments in MCMs, a user might actually be able to pick the desired MCM from a list of not-yet-loaded ones.
>>
>> (Hmm... maybe approximating a list of versions from interpolating the build version between two MCMs might work ...)
>>
>> Best,
>> Marcel
>>
>> Am 15.04.2021 02:13:34 schrieb Vanessa Freudenberg <[hidden email]>:
>>
>> Hi Christoph,
>>
>> Good idea, but is not quite that simple. The image "version number" does not uniquely identify a specific set of package versions. It's not enough to build a similar image again.
>>
>> To have somewhat reproducible builds (they're not bit-identical) you need to store the actual list of loaded package versions. The easiest way to do that would be using an MCM. Only when an explicit config map is deployed that indeed is a "fixed point" with a defined set of packages.
>>
>> MCMcmUpdater normally loads each explicit update map, plus then automatically updates the last map to the latest package versions. That is governed by the useLatestPackagesFrom: method.
>>
>> The latest config map appears to be
>> http://source.squeak.org/trunk/update-mt.485.mcm
>>
>> A method for reproducible builds could take e.g. 'mt.485' as an argument and should produce an image with version number 20313:
>>
>> ((MCRepository trunk versionNamed: 'update-mt.485.mcm') dependencies collect: [:dep | dep versionInfo versionNumber]) sum
>>
>>
>> –Vanessa–
>>
>>
>> On Thu, Apr 8, 2021 at 1:37 PM Thiede, Christoph <[hidden email]> wrote:
>>>
>>> Please review! :-)
>>>
>>>
>>> A concrete use case I was having in mind for this feature is to allow arbitrary Squeak Trunk version numbers in smalltalkCI, see: https://github.com/hpi-swa/smalltalkCI/pull/506
>>>
>>> At the moment, it is WIP - an unpolished prototype only, but the idea is that you could specify something like Squeak64-18242 instead of Squeak64-trunk in the CI/CLI args.
>>>
>>>
>>> Nevertheless, even if the proposal for SCI should get rejected, I think this is a helpful feature because releases are quite rare in Squeak and I find it important to tag certain states in the Trunk evolution.
>>>
>>> To improve support for the SCI PR, I would also like to backport these changes to 5.3 - there was only a single merge conflict, so if you agree with this proposal, I can send you a changeset for 5.3. Thanks in advance! :-)
>>>
>>> Best,
>>> Christoph
>>> ________________________________
>>> Von: Squeak-dev <[hidden email]> im Auftrag von [hidden email] <[hidden email]>
>>> Gesendet: Donnerstag, 8. April 2021 22:30:32
>>> An: [hidden email]
>>> Betreff: [squeak-dev] The Inbox: MonticelloConfigurations-ct.166.mcz
>>>
>>> A new version of MonticelloConfigurations was added to project The Inbox:
>>> http://source.squeak.org/inbox/MonticelloConfigurations-ct.166.mcz
>>>
>>> ==================== Summary ====================
>>>
>>> Name: MonticelloConfigurations-ct.166
>>> Author: ct
>>> Time: 8 April 2021, 10:30:30.702818 pm
>>> UUID: 6d887a86-fe0f-8e4c-a878-e40e8e612918
>>> Ancestors: MonticelloConfigurations-mt.165
>>>
>>> Adds support in MCMcmUpdater to install updates up to a specified system version number. This can be helpful when you want to bring an image into an exact state of an alpha version. I.e., a bug report or a tool can refer to Squeak6.0 Alpha #19570. Now it's possible to bring your 5.3 image into this state by evaluating:
>>>
>>>         MCMcmUpdater default doUpdate: true upToUpdate: 19570.
>>>
>>> Still, it is possible to install all updates or navigate forward to a certain upstream version using the existing protocol:
>>>
>>>         MCMcmUpdater default doUpdate: true.
>>>         MCMcmUpdater default doUpdate: true upTo: 470.
>>>
>>> Detailed changelog (anticlimax):
>>>
>>> - Added 'upToUpdate:' variant of update selectors on MCMcmUpdater.
>>> - Refactored existing update selectors to deduplicate the logic for doing a complete update (#doUpdate: etc.) versus installing all updates up to an upstream version (#doUpdate:upTo: etc.).
>>> - Added #currentSystemVersion and #totalSystemVersion on MCConfiguration to determine the current resp. eventual system version as implied by the update stream.
>>> - Renamed (+ deprecated) #depsSatisfying:version(s)Do:displayingProgress: due to misleading use of singular in the block keyword.
>>> - Improved multilingual support for the updater.
>>> - Improved documentation of the update protocol.
>>>
>>> Note that in some situations, a small number of follow-up updates will be installed that go beyond the specified system version number. This is caused by interdependencies between the target version and its postdecessors when a definiton was moved from one package into another one. See sender of MCReorganizationPreloader.
>>>
>>> =============== Diff against MonticelloConfigurations-mt.165 ===============
>>>
>>> Item was added:
>>> + ----- Method: MCConfiguration class>>ensureOpenTranscript:during: (in category 'preferences') -----
>>> + ensureOpenTranscript: aBoolean during: aBlock
>>> +
>>> +        | previous |
>>> +        previous := self ensureOpenTranscript.
>>> +        self ensureOpenTranscript: aBoolean.
>>> +        ^ aBlock ensure: [
>>> +                self ensureOpenTranscript: previous]!
>>>
>>> Item was added:
>>> + ----- Method: MCConfiguration>>currentSystemVersion (in category 'updating') -----
>>> + currentSystemVersion
>>> +        "Answer the current system version found in my configuration (or the associated working copy)."
>>> +
>>> +        ^ self systemVersionForDeps: (self dependencies
>>> +                collect: [:dep |
>>> +                        dep isFulfilledByAncestors
>>> +                                ifTrue: [dep versionInfo]
>>> +                                ifFalse: [dep package workingCopy ancestors] ] )!
>>>
>>> Item was added:
>>> + ----- Method: MCConfiguration>>depsSatisfying:upToUpdate:versionsDo:displayingProgress: (in category 'private') -----
>>> + depsSatisfying: selectBlock upToUpdate: anIntegerOrNil versionsDo: versionBlock displayingProgress: progressString
>>> +
>>> +        | count selectedVersions cleanWorkingCopies |
>>> +        self cacheAllFileNamesDuring: [
>>> +                self repositories do: [ :eachRepository |
>>> +                        MCRepositoryGroup default addRepository: eachRepository ].
>>> +
>>> +                "First, download selected versions"
>>> +                count := 0.
>>> +                selectedVersions := OrderedCollection new.
>>> +                self withProgress: progressString in: self dependencies do: [ :dep | | verName repo |
>>> +                        verName := dep versionInfo name.
>>> +                        self class extraProgressInfo ifTrue:
>>> +                                [ ProgressNotification signal: '' extra: ('Downloading {1}' translated format: {verName}) ].
>>> +                        repo := self repositories
>>> +                                detect: [ :eachRepository | eachRepository includesVersionNamed: verName ]
>>> +                                ifNone:  [ self logError: ('Version {1} not found in any repository' translated format: {verName}).
>>> +                                        self logError: 'Aborting' translated.
>>> +                                        ^ count ].
>>> +                        (selectBlock value: dep) ifTrue: [ | version |
>>> +                                version := self versionNamed: verName for: dep from: repo.
>>> +                                version ifNil: [ self logError: ('Could not download version {1} from {2}' translated format: {verName. repo description}).
>>> +                                        self logError: 'Aborting' translated.
>>> +                                        ^ count ].
>>> +                                dep package workingCopy newRepositoryGroupIfDefault. "fix old working copies"
>>> +                                dep package workingCopy repositoryGroup addRepository: repo.
>>> +                                selectedVersions add: version ] ].
>>> +
>>> +                "Then, process only those definitions that moved from one package to another, to avoid order dependence"
>>> +                cleanWorkingCopies := MCWorkingCopy allManagers select:
>>> +                        [ :wc | wc modified not and:
>>> +                                [ selectedVersions anySatisfy: [ :v | wc package = v package ] ] ].
>>> +                MCReorganizationPreloader preloadMovesBetween: selectedVersions.
>>> +
>>> +                "Finally, load/merge selected versions"
>>> +                [:exit | self withProgress: progressString in: selectedVersions do: [ :version |
>>> +                        self logUpdate: version package with: version.
>>> +                        self class extraProgressInfo ifTrue:
>>> +                                [ ProgressNotification signal: '' extra: ('Installing {1}' translated format: {version info name}) ].
>>> +                        versionBlock value: version.
>>> +                        count := count + 1.
>>> +                        anIntegerOrNil ifNotNil: [
>>> +                                self currentSystemVersion highestUpdate < anIntegerOrNil ifFalse: exit.
>>> +                                self flag: #optimization. "ct: By customizing the system version computation, we could save redundant version downloads above. However, because of the preloading stage, this might become a bit more sophisticated." ] ].
>>> +                ] valueWithExit.
>>> +
>>> +                "Clean up packages made dirty by MCReorganizationPreloader"
>>> +                cleanWorkingCopies
>>> +                        select: [ :wc | wc modified ]
>>> +                        thenDo: [ :wc | wc checkModified ].
>>> +        ].
>>> +        ^ count!
>>>
>>> Item was changed:
>>>   ----- Method: MCConfiguration>>depsSatisfying:versionDo:displayingProgress: (in category 'private') -----
>>> + depsSatisfying: selectBlock versionDo: versionBlock displayingProgress: progressString
>>> +
>>> +        self deprecated.
>>> +        ^ self
>>> +                depsSatisfying: selectBlock
>>> +                versionsDo: versionBlock
>>> +                displayingProgress: progressString!
>>> - depsSatisfying: selectBlock versionDo: verBlock displayingProgress: progressString
>>> -        | count selectedVersions cleanWorkingCopies |
>>> -        self cacheAllFileNamesDuring: [
>>> -                self repositories do: [ :eachRepository |
>>> -                        MCRepositoryGroup default addRepository: eachRepository ].
>>> -                "First, download selected versions"
>>> -                count := 0.
>>> -                selectedVersions := OrderedCollection new.
>>> -                self withProgress: progressString in: self dependencies do: [ :dep | | verName repo |
>>> -                        verName := dep versionInfo name.
>>> -                        self class extraProgressInfo ifTrue:
>>> -                                [ ProgressNotification signal: '' extra: 'Downloading ' , verName ].
>>> -                        repo := self repositories
>>> -                                detect: [ :eachRepository | eachRepository includesVersionNamed: verName ]
>>> -                                ifNone:  [ self logError: 'Version ' , verName , ' not found in any repository'.
>>> -                                        self logError: 'Aborting'.
>>> -                                        ^ count ].
>>> -                        (selectBlock value: dep) ifTrue: [ | version |
>>> -                                version := self versionNamed: verName for: dep from: repo.
>>> -                                version ifNil: [ self logError: 'Could not download version ' , verName , ' from ' , repo description.
>>> -                                        self logError: 'Aborting'.
>>> -                                        ^ count ].
>>> -                                dep package workingCopy newRepositoryGroupIfDefault. "fix old working copies"
>>> -                                dep package workingCopy repositoryGroup addRepository: repo.
>>> -                                selectedVersions add: version]].
>>> -                "Then, process only those definitions that moved from one package to another, to avoid order dependence"
>>> -                cleanWorkingCopies := MCWorkingCopy allManagers select:
>>> -                        [ :wc | wc modified not and:
>>> -                                [ selectedVersions anySatisfy: [ :v | wc package = v package ] ] ].
>>> -                MCReorganizationPreloader preloadMovesBetween: selectedVersions.
>>> -                "Finally, load/merge selected versions"
>>> -                self withProgress: progressString in: selectedVersions do: [ :version |
>>> -                        self logUpdate: version package with: version.
>>> -                        self class extraProgressInfo ifTrue:
>>> -                                [ ProgressNotification signal: '' extra: 'Installing ' , version info name ].
>>> -                        verBlock value: version.
>>> -                        count := count + 1 ].
>>> -                "Clean up packages made dirty by MCReorganizationPreloader"
>>> -                cleanWorkingCopies
>>> -                        select: [ :wc | wc modified ]
>>> -                        thenDo: [ :wc | wc checkModified ].
>>> -        ].
>>> -        ^ count!
>>>
>>> Item was added:
>>> + ----- Method: MCConfiguration>>depsSatisfying:versionsDo:displayingProgress: (in category 'private') -----
>>> + depsSatisfying: selectBlock versionsDo: versionBlock displayingProgress: progressString
>>> +
>>> +        ^ self
>>> +                depsSatisfying: selectBlock
>>> +                upToUpdate: nil
>>> +                versionsDo: versionBlock
>>> +                displayingProgress: progressString!
>>>
>>> Item was changed:
>>>   ----- Method: MCConfiguration>>load (in category 'actions') -----
>>>   load
>>>          ^self depsSatisfying: [:dep | dep isCurrent not]
>>> +                versionsDo: [:ver | ver load]
>>> +                displayingProgress: 'loading packages' translated!
>>> -                versionDo: [:ver | ver load]
>>> -                displayingProgress: 'loading packages'
>>> - !
>>>
>>> Item was changed:
>>>   ----- Method: MCConfiguration>>merge (in category 'actions') -----
>>>   merge
>>>          ^self depsSatisfying: [:dep | dep isFulfilledByAncestors not]
>>> +                versionsDo: [:ver | ver merge]
>>> +                displayingProgress: 'merging packages' translated!
>>> -                versionDo: [:ver | ver merge]
>>> -                displayingProgress: 'merging packages'
>>> - !
>>>
>>> Item was changed:
>>>   ----- Method: MCConfiguration>>setSystemVersion (in category 'updating') -----
>>>   setSystemVersion
>>> -        "Set the current system version date to the latest date found in my configuration (or the associated working copy). Also set the highest update number to the sum of version numbers in my configuration."
>>>
>>> +        | newVersion |
>>> +        newVersion := self currentSystemVersion.
>>> -        | versionNumbers versionDates |
>>> -        versionNumbers := self dependencies collect: [:d |
>>> -                (d versionInfo name copyAfterLast: $.) asInteger].
>>> -        versionDates := self dependencies collect: [:d |
>>> -                d versionInfo date
>>> -                        ifNil: [d package workingCopy ancestors first date]].
>>>          SystemVersion current
>>> +                date: newVersion date;
>>> +                highestUpdate: newVersion highestUpdate.!
>>> -                date: versionDates max;
>>> -                highestUpdate: versionNumbers sum.!
>>>
>>> Item was added:
>>> + ----- Method: MCConfiguration>>systemVersionForDeps: (in category 'updating') -----
>>> + systemVersionForDeps: dependencies
>>> +        "Answer the system version that will be reached by loading the passed dependencies."
>>> +
>>> +        | versionNumbers versionDates |
>>> +        versionNumbers := dependencies collect: [:dep |
>>> +                dep isCollection
>>> +                        ifFalse: [dep name versionNumber]
>>> +                        ifTrue: [(dep collect: [:version |
>>> +                                version name versionNumber])
>>> +                                        ifEmpty: [0]
>>> +                                        ifNotEmpty: #max]].
>>> +        versionDates := dependencies with: self dependencies collect: [:dep :originalDep |
>>> +                (dep isCollection ifFalse: [dep date])
>>> +                        ifNil: [originalDep package workingCopy ancestors
>>> +                                        ifEmpty: [nil]
>>> +                                        ifNotEmpty: [:ancestors | ancestors first date]]].
>>> +
>>> +        ^ SystemVersion new
>>> +                date: ((versionDates copyWithout: nil)
>>> +                        ifEmpty: [nil]
>>> +                        ifNotEmpty: #max);
>>> +                highestUpdate: versionNumbers sum!
>>>
>>> Item was added:
>>> + ----- Method: MCConfiguration>>totalSystemVersion (in category 'updating') -----
>>> + totalSystemVersion
>>> +        "Answer the total system version that will be reached by upgrading the receiver."
>>> +
>>> +        ^ self systemVersionForDeps: (self dependencies collect: #versionInfo)!
>>>
>>> Item was changed:
>>>   ----- Method: MCConfiguration>>upgrade (in category 'actions') -----
>>>   upgrade
>>> +
>>> +        ^ self upgradeUpToUpdate: nil!
>>> -        ^self depsSatisfying:
>>> -                        [:dep | dep isFulfilledByAncestors not]
>>> -                versionDo:
>>> -                        [:ver |
>>> -                        (self class upgradeIsMerge and: [ver shouldMerge])
>>> -                                ifFalse: [ver load]
>>> -                                ifTrue:
>>> -                                        [[ver merge]
>>> -                                                on: MCNoChangesException
>>> -                                                do: [:req| req resume ]
>>> -                                                on: MCMergeResolutionRequest
>>> -                                                do: [:request |
>>> -                                                        request merger conflicts isEmpty
>>> -                                                                ifTrue: [request resume: true]
>>> -                                                                ifFalse: [request pass]]
>>> -                                                on: Deprecation
>>> -                                                do: [:req| req resume ]]]
>>> -                displayingProgress: 'upgrading packages'!
>>>
>>> Item was added:
>>> + ----- Method: MCConfiguration>>upgradeUpToUpdate: (in category 'actions') -----
>>> + upgradeUpToUpdate: anIntegerOrNil
>>> +
>>> +        ^ self
>>> +                depsSatisfying: [:dep |
>>> +                        dep isFulfilledByAncestors not]
>>> +                upToUpdate: anIntegerOrNil
>>> +                versionsDo: [:ver |
>>> +                        (self class upgradeIsMerge and: [ver shouldMerge])
>>> +                                ifFalse: [ver load]
>>> +                                ifTrue:
>>> +                                        [[ver merge]
>>> +                                                on: MCNoChangesException
>>> +                                                do: [:req | req resume ]
>>> +                                                on: MCMergeResolutionRequest
>>> +                                                do: [:request |
>>> +                                                        request merger conflicts isEmpty
>>> +                                                                ifTrue: [request resume: true]
>>> +                                                                ifFalse: [request pass]]
>>> +                                                on: Deprecation
>>> +                                                do: [:req | req resume ]]]
>>> +                displayingProgress: 'upgrading packages' translated!
>>>
>>> Item was added:
>>> + ----- Method: MCMcmUpdater>>basicDoUpdate:do: (in category 'updating') -----
>>> + basicDoUpdate: interactive do: aBlock
>>> +        "Update the image by evaluating aBlock. If this is the default updater for the system, update the system version when complete. If interactive, use a modal notifier, otherwise only update the transcript. Flush all caches. If a previous download failed, this is often helpful."
>>> +
>>> +        | config previousUpdateLevel |
>>> +        previousUpdateLevel := SystemVersion current highestUpdate.
>>> +        MCFileBasedRepository flushAllCaches.
>>> +
>>> +        MCConfiguration ensureOpenTranscript: interactive during: [
>>> +                config := aBlock value.
>>> +
>>> +                config ifNil: [
>>> +                        interactive ifTrue: [^ self inform: 'Unable to retrieve updates from remote repository.' translated].
>>> +                        Transcript cr; show: '==========  Unable to retrieve updates from remote repository. ==========' translated; cr.
>>> +                        ^ self ].
>>> +
>>> +                MCMcmUpdater default == self
>>> +                        ifTrue: [
>>> +                                config setSystemVersion.
>>> +                                interactive ifTrue: [
>>> +                                        self inform: (self updateMessageFor: previousUpdateLevel)].
>>> +                                Transcript cr;
>>> +                                        show: '==========  Update completed:  ' translated;
>>> +                                        show: previousUpdateLevel;
>>> +                                        show: ' -> ' ;
>>> +                                        show: SystemVersion current highestUpdate;
>>> +                                        show: ' =========='; cr ]
>>> +                        ifFalse: [
>>> +                                interactive
>>> +                                        ifTrue: [ self inform: 'Update completed.' ].
>>> +                                Transcript cr; show: '==========  Update completed. ==========' translated; cr ] ].!
>>>
>>> Item was added:
>>> + ----- Method: MCMcmUpdater>>basicUpdateFromRepositories:do: (in category 'updating') -----
>>> + basicUpdateFromRepositories: repositoryUrls do: repositoryBlock
>>> +
>>> +        | repos config |
>>> +        MCConfiguration upgradeIsMerge: true.
>>> +        "The list of repositories to consult in order"
>>> +        repos := repositoryUrls collect: [:url|
>>> +                MCRepositoryGroup default repositories
>>> +                        detect: [:repo | repo description = url]
>>> +                        ifNone: [ | repo |
>>> +                                repo := MCHttpRepository location: url.
>>> +                                MCRepositoryGroup default addRepository: repo.
>>> +                                repo]].
>>> +
>>> +        "The list of updates-author.version.mcm sorted by version"
>>> +        repos do: [:repo |
>>> +                config := repositoryBlock value: repo].
>>> +        ^ config!
>>>
>>> Item was added:
>>> + ----- Method: MCMcmUpdater>>basicUpdateFromRepository:thenDo: (in category 'updating') -----
>>> + basicUpdateFromRepository: aRepository thenDo: configBlock
>>> +
>>> +        ^ self
>>> +                basicUpdateFromRepository: aRepository
>>> +                whileUpdatesSatisfy: [true]
>>> +                upToUpdate: nil
>>> +                thenDo: configBlock!
>>>
>>> Item was added:
>>> + ----- Method: MCMcmUpdater>>basicUpdateFromRepository:whileUpdatesSatisfy:upToUpdate:thenDo: (in category 'updating') -----
>>> + basicUpdateFromRepository: aRepository whileUpdatesSatisfy: updateBlock upToUpdate: anIntegerOrNil thenDo: configBlock
>>> +
>>> +        | config |
>>> +        aRepository cacheAllFileNamesDuring: [ | updateList |
>>> +                updateList := self updateListFor: aRepository.
>>> +                "Proceed only if there are updates available at all."
>>> +                updateList ifNotEmpty: [
>>> +                        updateList := self refreshUpdateMapFor: aRepository with: updateList.
>>> +                        "Now process each update file. Check if we have all dependencies and if not, load the entire configuration (this is mostly to skip older updates quickly)"
>>> +                        [:exit | updateList
>>> +                                do: [:assoc |
>>> +                                        (updateBlock cull: assoc key cull: assoc value) ifFalse: exit.
>>> +                                        ProgressNotification signal: '' extra: ('Processing {1}' translated format: {assoc value}).
>>> +                                        config := aRepository versionNamed: assoc value.
>>> +                                        anIntegerOrNil ifNotNil: [
>>> +                                                config currentSystemVersion highestUpdate <= anIntegerOrNil ifFalse: exit].
>>> +                                        self updateFromConfig: config upToUpdate: anIntegerOrNil.
>>> +                                        anIntegerOrNil ifNotNil: [
>>> +                                                config currentSystemVersion highestUpdate < config totalSystemVersion highestUpdate ifTrue: exit].
>>> +                                        self lastUpdateMap at: aRepository description put: assoc key]
>>> +                                displayingProgress: 'Processing configurations' translated] valueWithExit.
>>> +                        configBlock cull: config]].
>>> +        ^ config!
>>>
>>> Item was changed:
>>>   ----- Method: MCMcmUpdater>>doUpdate: (in category 'updating') -----
>>>   doUpdate: interactive
>>> +        "Update the image by loading all pending updates from the server."
>>> -        "Update the image by loading all pending updates from the server. If this is
>>> -        the default updater for the system, update the system version when complete.
>>> -        If interteractive use a modal notifier, otherwise only update the transcript.
>>> -        Flush all caches. If a previous download failed this is often helpful"
>>>
>>> +        ^ self basicDoUpdate: interactive do: [
>>> +                self updateFromRepository ]!
>>> -        | config previousUpdateLevel ensureTranscriptSetting |
>>> -        previousUpdateLevel := SystemVersion current highestUpdate.
>>> -        MCFileBasedRepository flushAllCaches.
>>> -        ensureTranscriptSetting := MCConfiguration ensureOpenTranscript.
>>> -        [ MCConfiguration ensureOpenTranscript: interactive.
>>> -        config := self updateFromRepository.
>>> -        config ifNil: [
>>> -                interactive ifTrue: [ ^self inform: 'Unable to retrieve updates from remote repository.' translated ].
>>> -                Transcript cr; show: '==========  Unable to retrieve updates from remote repository. ==========' translated; cr.
>>> -                ^ self ].
>>> -        MCMcmUpdater default == self
>>> -                ifTrue: [
>>> -                        config setSystemVersion.
>>> -                        interactive ifTrue: [
>>> -                                self inform: (self updateMessageFor: previousUpdateLevel)].
>>> -                        Transcript cr;
>>> -                                show: '==========  Update completed:  ' translated;
>>> -                                show: previousUpdateLevel;
>>> -                                show: ' -> ' ;
>>> -                                show: SystemVersion current highestUpdate;
>>> -                                show: ' =========='; cr ]
>>> -                ifFalse: [
>>> -                        interactive
>>> -                                ifTrue: [ self inform: 'Update completed.' ].
>>> -                        Transcript cr; show: '==========  Update completed. ==========' translated; cr ] ]
>>> -                ensure: [ MCConfiguration ensureOpenTranscript: ensureTranscriptSetting].
>>> -
>>> -        !
>>>
>>> Item was changed:
>>>   ----- Method: MCMcmUpdater>>doUpdate:upTo: (in category 'updating') -----
>>>   doUpdate: interactive upTo: versionNumber
>>> +        "Update the image by loading all pending updates from the server until update stream with the specified version number has been processed."
>>> -        "Update the image by loading all pending updates from the server. If this is
>>> -        the default updater for the system, update the system version when complete.
>>> -        If interteractive use a modal notifier, otherwise only update the transcript.
>>> -        Flush all caches. If a previous download failed this is often helpful"
>>>
>>> +        ^ self basicDoUpdate: interactive do: [
>>> +                self updateFromRepositories: {self repository} upTo: versionNumber ]!
>>> -        | config previousUpdateLevel |
>>> -        previousUpdateLevel := SystemVersion current highestUpdate.
>>> -        MCFileBasedRepository flushAllCaches.
>>> -        config := self updateFromRepositories: { self repository } upTo: versionNumber.
>>> -        config ifNil: [
>>> -                interactive ifTrue: [ ^self inform: 'Unable to retrieve updates from remote repository.' translated ].
>>> -                Transcript cr; show: '==========  Unable to retrieve updates from remote repository. ==========' translated; cr.
>>> -                ^ self ].
>>> -        MCMcmUpdater default == self
>>> -                ifTrue: [
>>> -                        config setSystemVersion.
>>> -                        interactive ifTrue: [
>>> -                                self inform: (self updateMessageFor: previousUpdateLevel)].
>>> -                        Transcript cr;
>>> -                                show: '==========  Update completed:  ' translated;
>>> -                                show: previousUpdateLevel;
>>> -                                show: ' -> ' ;
>>> -                                show: SystemVersion current highestUpdate;
>>> -                                show: ' =========='; cr ]
>>> -                ifFalse: [
>>> -                        interactive
>>> -                                ifTrue: [ self inform: 'Update completed.' ].
>>> -                        Transcript cr; show: '==========  Update completed. ==========' translated; cr ]
>>> -        !
>>>
>>> Item was added:
>>> + ----- Method: MCMcmUpdater>>doUpdate:upToUpdate: (in category 'updating') -----
>>> + doUpdate: interactive upToUpdate: anIntegerOrNil
>>> +        "Update the image by loading all pending updates from the server until the system version has reached at least anInteger. If anInteger is nil, install all available updates."
>>> +
>>> +        ^ self basicDoUpdate: interactive do: [
>>> +                self updateFromRepositories: {self repository} upToUpdate: anIntegerOrNil ]!
>>>
>>> Item was changed:
>>>   ----- Method: MCMcmUpdater>>doUpdateUpTo: (in category 'updating') -----
>>>   doUpdateUpTo: versionNumber
>>> +        "Update the image by loading all pending updates from the server until update stream with the specified version number has been processed."
>>> -        "Update the image by loading all pending updates from the server. If this is
>>> -        the default updater for the system, update the system version when complete.
>>> -        Flush all caches. If a previous download failed this is often helpful"
>>>
>>>          ^self doUpdate: true upTo: versionNumber
>>>   !
>>>
>>> Item was changed:
>>>   ----- Method: MCMcmUpdater>>updateFromConfig: (in category 'updating') -----
>>>   updateFromConfig: config
>>>
>>> +        ^ self updateFromConfig: config upToUpdate: nil!
>>> -        "Skip packages that were specifically unloaded"
>>> -        config dependencies: (config dependencies
>>> -                reject: [:dep| self class skipPackages includes: dep package name]).
>>> -        self class updateMissingPackages ifFalse:[
>>> -                "Skip packages that are not in the image"
>>> -                config dependencies: (config dependencies
>>> -                        select: [:dep| dep package hasWorkingCopy])].
>>> -        (config dependencies allSatisfy:[:dep| dep isFulfilled])
>>> -                ifFalse:[config upgrade].
>>> - !
>>>
>>> Item was added:
>>> + ----- Method: MCMcmUpdater>>updateFromConfig:upToUpdate: (in category 'updating') -----
>>> + updateFromConfig: config upToUpdate: anIntegerOrNil
>>> +
>>> +        "Skip packages that were specifically unloaded"
>>> +        config dependencies: (config dependencies
>>> +                reject: [:dep | self class skipPackages includes: dep package name]).
>>> +        self class updateMissingPackages ifFalse: [
>>> +                "Skip packages that are not in the image"
>>> +                config dependencies: (config dependencies
>>> +                        select: [:dep | dep package hasWorkingCopy])].
>>> +        (config dependencies allSatisfy: [:dep | dep isFulfilled])
>>> +                ifFalse: [config upgradeUpToUpdate: anIntegerOrNil].!
>>>
>>> Item was changed:
>>>   ----- Method: MCMcmUpdater>>updateFromRepositories:upTo: (in category 'updating') -----
>>>   updateFromRepositories: repositoryUrls upTo: versionNumber
>>> +        "
>>> +        MCMcmUpdater default updateFromRepositories: #('http://squeaksource.com/MCUpdateTest') upTo: 3
>>> +        "
>>> -        "MCMcmUpdater updateFromRepositories: #(
>>> -                'http://squeaksource.com/MCUpdateTest'
>>> -        )"
>>>
>>> +        ^ self basicUpdateFromRepositories: repositoryUrls do: [:repo |
>>> +                self updateFromRepository: repo upTo: versionNumber]!
>>> -        | repos config |
>>> -        MCConfiguration upgradeIsMerge: true.
>>> -        "The list of repositories to consult in order"
>>> -        repos := repositoryUrls collect:[:url|
>>> -                MCRepositoryGroup default repositories
>>> -                        detect:[:r| r description = url]
>>> -                        ifNone:[ | r |
>>> -                                r := MCHttpRepository location: url user: '' password: ''.
>>> -                                MCRepositoryGroup default addRepository: r.
>>> -                                r]].
>>> -
>>> -        "The list of updates-author.version.mcm sorted by version"
>>> -        repos do:[ :r | config := self updateFromRepository: r upTo: versionNumber].
>>> -        ^config!
>>>
>>> Item was added:
>>> + ----- Method: MCMcmUpdater>>updateFromRepositories:upToUpdate: (in category 'updating') -----
>>> + updateFromRepositories: repositoryUrls upToUpdate: anIntegerOrNil
>>> +        "
>>> +        MCMcmUpdater default updateFromRepositories: #('http://squeaksource.com/MCUpdateTest') upToUpdate: 2130
>>> +        "
>>> +
>>> +        ^ self basicUpdateFromRepositories: repositoryUrls do: [:repo |
>>> +                self updateFromRepository: repo upToUpdate: anIntegerOrNil ]!
>>>
>>> Item was changed:
>>>   ----- Method: MCMcmUpdater>>updateFromRepository (in category 'updating') -----
>>>   updateFromRepository
>>>
>>> +        ^ self updateFromRepository: self getRepositoryFromRepositoryGroup!
>>> -        | config repo |
>>> -        repo := self getRepositoryFromRepositoryGroup.
>>> -        repo cacheAllFileNamesDuring: [ | updateList |
>>> -                updateList := self updateListFor: repo.
>>> -                "Proceed only if there are updates available at all."
>>> -                updateList ifNotEmpty: [
>>> -                        updateList := self refreshUpdateMapFor: repo with: updateList.
>>> -                        "Now process each update file. Check if we have all dependencies and if not,
>>> -                        load the entire configuration (this is mostly to skip older updates quickly)"
>>> -                        updateList do:[:assoc|
>>> -                                ProgressNotification signal: '' extra: 'Processing ', assoc value.
>>> -                                config := repo versionNamed: assoc value.
>>> -                                self updateFromConfig: config.
>>> -                                self lastUpdateMap at: repo description put: assoc key.
>>> -                        ] displayingProgress: 'Processing configurations'.
>>> -                        "We've loaded all the provided update configurations.
>>> -                        Use the latest configuration to update all the remaining packages."
>>> -                        (self useLatestPackagesFrom: repo) ifTrue: [
>>> -                                config updateFromRepositories.
>>> -                                config upgrade].
>>> -                ]].
>>> -        ^ config
>>> - !
>>>
>>> Item was added:
>>> + ----- Method: MCMcmUpdater>>updateFromRepository: (in category 'updating') -----
>>> + updateFromRepository: aRepository
>>> +
>>> +        ^ self basicUpdateFromRepository: aRepository thenDo: [:config |
>>> +                "We've loaded all the provided update configurations. Use the latest configuration to update all the remaining packages."
>>> +                (self useLatestPackagesFrom: aRepository) ifTrue: [
>>> +                        config updateFromRepositories.
>>> +                        config upgrade]]!
>>>
>>> Item was changed:
>>>   ----- Method: MCMcmUpdater>>updateFromRepository:upTo: (in category 'updating') -----
>>> + updateFromRepository: aRepository upTo: versionNumber
>>> - updateFromRepository: repository upTo: versionNumber
>>>
>>> +        ^ self
>>> +                basicUpdateFromRepository: aRepository
>>> +                whileUpdatesSatisfy: [:updateVersion | updateVersion <= versionNumber]
>>> +                upToUpdate: nil
>>> +                thenDo: []!
>>> -        | config |
>>> -        repository cacheAllFileNamesDuring: [ | updateList |
>>> -                updateList := self updateListFor: repository.
>>> -                "Proceed only if there are updates available at all."
>>> -                updateList ifNotEmpty: [
>>> -                        updateList := self refreshUpdateMapFor: repository with: updateList.
>>> -                        "Now process each update file. Check if we have all dependencies and if not,
>>> -                        load the entire configuration (this is mostly to skip older updates quickly)"
>>> -                        updateList do:[:assoc|
>>> -                                assoc key > versionNumber ifTrue: [^config].
>>> -                                ProgressNotification signal: '' extra: 'Processing ', assoc value.
>>> -                                config := repository versionNamed: assoc value.
>>> -                                self updateFromConfig: config.
>>> -                                self lastUpdateMap at: repository description put: assoc key.
>>> -                        ] displayingProgress: 'Processing configurations'.
>>> -                ]].
>>> -        ^config
>>> - !
>>>
>>> Item was added:
>>> + ----- Method: MCMcmUpdater>>updateFromRepository:upToUpdate: (in category 'updating') -----
>>> + updateFromRepository: aRepository upToUpdate: anIntegerOrNil
>>> +
>>> +        ^ self
>>> +                basicUpdateFromRepository: aRepository
>>> +                whileUpdatesSatisfy: [true]
>>> +                upToUpdate: anIntegerOrNil
>>> +                thenDo: []!
>>>
>>>
>>>
>>
>