SystemOrganization addCategory: #'Gofer-Core'! SystemOrganization addCategory: #'Gofer-Test'! Error subclass: #GoferRepositoryError instanceVariableNames: 'repository' classVariableNames: '' poolDictionaries: '' category: 'Gofer-Core'! !GoferRepositoryError class methodsFor: 'instance creation' stamp: 'lr 12/9/2009 19:15'! signal: aString repository: aRepository ^ self new repository: aRepository; signal: aString! ! !GoferRepositoryError methodsFor: 'private' stamp: 'lr 12/9/2009 20:57'! defaultAction ^ #()! ! !GoferRepositoryError methodsFor: 'private' stamp: 'lr 12/9/2009 22:32'! isResumable ^ true! ! !GoferRepositoryError methodsFor: 'accessing' stamp: 'lr 12/9/2009 19:14'! repository ^ repository! ! !GoferRepositoryError methodsFor: 'accessing' stamp: 'lr 12/9/2009 19:14'! repository: aRepository repository := aRepository! ! TestResource subclass: #GoferResource instanceVariableNames: 'versionReferences monticelloRepository repositoryConfiguration' classVariableNames: '' poolDictionaries: '' category: 'Gofer-Test'! !GoferResource methodsFor: 'accessing' stamp: 'lr 12/12/2009 10:37'! monticelloRepository ^ monticelloRepository! ! !GoferResource methodsFor: 'accessing' stamp: 'lr 12/13/2009 13:27'! repositoryConfiguration ^ repositoryConfiguration! ! !GoferResource methodsFor: 'running' stamp: 'lr 12/13/2009 13:26'! setUp super setUp. self setUpVersionReferences; setUpMonticelloRepository; setUpRepositoryConfiguration! ! !GoferResource methodsFor: 'running' stamp: 'TestRunner 12/13/2009 14:45'! setUpMonticelloRepository "This method builds a fake repository with the version references from #buildReferences." monticelloRepository := MCDictionaryRepository new. versionReferences do: [ :reference | monticelloRepository basicStoreVersion: (MCVersion new setPackage: (MCPackage new name: reference package) info: (MCVersionInfo name: reference name id: UUID new message: 'This is a mock version' date: Date today time: Time now author: reference author ancestors: #()) snapshot: (MCSnapshot fromDefinitions: (Array with: (MCOrganizationDefinition categories: (Array with: reference package asSymbol)) with: (MCClassDefinition name: (reference package replaceAll: '-' with: '') asSymbol superclassName: #Object category: reference package asSymbol instVarNames: #() comment: ''))) dependencies: #()) ]! ! !GoferResource methodsFor: 'running' stamp: 'lr 12/13/2009 13:26'! setUpRepositoryConfiguration repositoryConfiguration := GoferRepositoryConfiguration new. repositoryConfiguration disableCache; addRepository: monticelloRepository! ! !GoferResource methodsFor: 'running' stamp: 'TestRunner 12/12/2009 10:57'! setUpVersionReferences "This method answers a set of Gofer references in the order they should be sorted. It includes two different packages (Gofer-Foo, Gofer-Bar), linear series of packages (Gofer-Foo-lr.1, Gofer-Foo-lr.2, Gofer-Foo-lr.4), packages with a branch (Gofer-Bar-lr.branch.1,Gofer-Bar-lr.branch.2), and packages with the same version but different authors (Gofer-Bar-jf.1, Gofer-Bar-lr.1)." versionReferences := OrderedCollection new. versionReferences add: (GoferVersionReference name: 'Gofer-Bar-lr.branch.1'); add: (GoferVersionReference name: 'Gofer-Bar-lr.branch.2'); add: (GoferVersionReference name: 'Gofer-Bar-jf.1'); add: (GoferVersionReference name: 'Gofer-Bar-lr.1'); add: (GoferVersionReference name: 'Gofer-Foo-lr.1'); add: (GoferVersionReference name: 'Gofer-Foo-lr.2'); add: (GoferVersionReference name: 'Gofer-Foo-lr.4')! ! !GoferResource methodsFor: 'accessing' stamp: 'lr 12/12/2009 10:37'! versionReferences ^ versionReferences! ! !MCDirectoryRepository methodsFor: '*gofer-accessing' stamp: 'lr 12/11/2009 22:32'! goferPriority ^ 5! ! !MCFileBasedRepository methodsFor: '*gofer-accessing' stamp: 'TestRunner 12/12/2009 11:12'! goferReferences | versionNames | versionNames := [ self allVersionNames ] on: Error do: [ :error | ^ GoferRepositoryError signal: error messageText repository: self ]. ^ versionNames collect: [ :each | GoferResolvedReference name: each repository: self ]! ! !MCFileBasedRepository methodsFor: '*gofer-accessing' stamp: 'lr 12/12/2009 11:29'! goferVersionFrom: aVersionReference ^ self loadVersionFromFileNamed: aVersionReference name , '.mcz'! ! !MCVersionLoader methodsFor: '*gofer' stamp: 'dkh 10/12/2009 12:54'! goferHasVersions ^ versions isEmpty not! ! TestCase subclass: #GoferTest instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'Gofer-Test'! GoferTest subclass: #GoferApiTest instanceVariableNames: 'gofer' classVariableNames: '' poolDictionaries: '' category: 'Gofer-Test'! !GoferApiTest methodsFor: 'utilities' stamp: 'TestRunner 12/13/2009 13:40'! assert: aGofer repositories: anArray self assert: aGofer repository repositories size = anArray size. aGofer repository repositories with: anArray do: [ :first :second | self assert: first description = second ]! ! !GoferApiTest methodsFor: 'accessing' stamp: 'lr 12/13/2009 13:21'! gofer ^ gofer! ! !GoferApiTest methodsFor: 'running' stamp: 'lr 12/13/2009 13:21'! setUp super setUp. gofer := Gofer new. gofer repository disableCache! ! !GoferApiTest methodsFor: 'testing-references' stamp: 'TestRunner 12/13/2009 14:00'! testConstraintReference gofer repository: self monticelloRepository; package: 'Gofer-Bar' constraint: [ :ref | ref branch = 'branch' ]. self assert: (gofer resolved size = 1). self assert: (gofer resolved first isKindOf: GoferResolvedReference). self assert: (gofer resolved first package = 'Gofer-Bar'). self assert: (gofer resolved first author = 'lr'). self assert: (gofer resolved first branch = 'branch'). self assert: (gofer resolved first version = 2). self assert: (gofer resolved first repository = self monticelloRepository)! ! !GoferApiTest methodsFor: 'testing-repositories' stamp: 'lr 12/13/2009 13:36'! testCroquet gofer croquet: 'Hermes'. self assert: gofer repositories: #('http://hedgehog.software.umn.edu:8888/Hermes')! ! !GoferApiTest methodsFor: 'testing-repositories' stamp: 'lr 12/13/2009 13:54'! testCustomRepository gofer repository: self monticelloRepository. self assert: gofer repositories: (Array with: self monticelloRepository description). self assert: (gofer repository repositories first = self monticelloRepository)! ! !GoferApiTest methodsFor: 'testing-repositories' stamp: 'lr 12/13/2009 13:51'! testDirectoryRepository gofer directory: FileDirectory default pathName. self assert: gofer repositories: (Array with: FileDirectory default pathName). self assert: (gofer repository repositories first isKindOf: MCDirectoryRepository)! ! !GoferApiTest methodsFor: 'testing-repositories' stamp: 'lr 12/13/2009 13:51'! testFtpRepository gofer url: 'ftp://squeakfoundation.org/39a' username: 'foo' password: 'bar'. self assert: gofer repositories: #('ftp://foo@squeakfoundation.org/39a'). self assert: (gofer repository repositories first isKindOf: MCFtpRepository)! ! !GoferApiTest methodsFor: 'testing-repositories' stamp: 'lr 12/13/2009 13:36'! testGemsource gofer gemsource: 'Seaside29'. self assert: gofer repositories: #('http://seaside.gemstone.com/ss/Seaside29')! ! !GoferApiTest methodsFor: 'testing-repositories' stamp: 'lr 12/13/2009 13:51'! testHttpRepository gofer url: 'http://source.lukas-renggli.ch/pier' username: 'foo' password: 'bar'. self assert: gofer repositories: #('http://source.lukas-renggli.ch/pier'). self assert: (gofer repository repositories first isKindOf: MCHttpRepository)! ! !GoferApiTest methodsFor: 'testing-repositories' stamp: 'lr 12/13/2009 13:36'! testImpara gofer impara: 'Tweak'. self assert: gofer repositories: #('http://source.impara.de/Tweak')! ! !GoferApiTest methodsFor: 'testing' stamp: 'TestRunner 12/13/2009 15:59'! testInitialized self assert: (gofer references isEmpty). self assert: (gofer repository isKindOf: GoferRepositoryConfiguration)! ! !GoferApiTest methodsFor: 'testing-references' stamp: 'lr 12/13/2009 13:59'! testPackageReference gofer repository: self monticelloRepository; package: 'Gofer-Foo'. self assert: (gofer resolved size = 1). self assert: (gofer resolved first isKindOf: GoferResolvedReference). self assert: (gofer resolved first package = 'Gofer-Foo'). self assert: (gofer resolved first author = 'lr'). self assert: (gofer resolved first branch isEmpty). self assert: (gofer resolved first version = 4). self assert: (gofer resolved first repository = self monticelloRepository)! ! !GoferApiTest methodsFor: 'testing-repositories' stamp: 'lr 12/13/2009 13:36'! testRenggli gofer renggli: 'pier'. self assert: gofer repositories: #('http://source.lukas-renggli.ch/pier')! ! !GoferApiTest methodsFor: 'testing-repositories' stamp: 'lr 12/13/2009 13:36'! testSaltypickle gofer saltypickle: 'GraphViz'. self assert: gofer repositories: #('http://squeak.saltypickle.com/GraphViz')! ! !GoferApiTest methodsFor: 'testing-repositories' stamp: 'lr 12/13/2009 13:36'! testSqueakfoundation gofer squeakfoundation: '39a'. self assert: gofer repositories: #('http://source.squeakfoundation.org/39a')! ! !GoferApiTest methodsFor: 'testing-repositories' stamp: 'lr 12/13/2009 13:36'! testSqueaksource gofer squeaksource: 'Seaside29'. self assert: gofer repositories: #('http://www.squeaksource.com/Seaside29')! ! !GoferApiTest methodsFor: 'testing-repositories' stamp: 'lr 12/13/2009 13:51'! testSubDirectoryRepository gofer directory: FileDirectory default pathName , FileDirectory slash , '*'. self assert: gofer repositories: (Array with: FileDirectory default pathName , FileDirectory slash , '*'). self assert: (gofer repository repositories first isKindOf: MCSubDirectoryRepository)! ! !GoferApiTest methodsFor: 'testing-references' stamp: 'lr 12/13/2009 14:01'! testVersionReference gofer repository: self monticelloRepository; version: 'Gofer-Foo-lr.2'. self assert: (gofer resolved size = 1). self assert: (gofer resolved first isKindOf: GoferResolvedReference). self assert: (gofer resolved first package = 'Gofer-Foo'). self assert: (gofer resolved first author = 'lr'). self assert: (gofer resolved first branch isEmpty). self assert: (gofer resolved first version = 2). self assert: (gofer resolved first repository = self monticelloRepository)! ! !GoferApiTest methodsFor: 'testing-repositories' stamp: 'lr 12/13/2009 13:36'! testWiresong gofer wiresong: 'ob'. self assert: gofer repositories: #('http://source.wiresong.ca/ob')! ! GoferTest subclass: #GoferOperationTest instanceVariableNames: 'gofer' classVariableNames: '' poolDictionaries: '' category: 'Gofer-Test'! !GoferOperationTest methodsFor: 'assertions' stamp: 'TestRunner 12/13/2009 14:17'! assertCategory: aSymbol self assert: (Smalltalk organization categories includes: aSymbol)! ! !GoferOperationTest methodsFor: 'assertions' stamp: 'lr 8/20/2009 20:58'! assertClass: aClassSymbol self assert: (Smalltalk hasClassNamed: aClassSymbol)! ! !GoferOperationTest methodsFor: 'assertions' stamp: 'lr 8/20/2009 21:04'! assertClass: aClassSymbol selector: aMethodSymbol self assertClass: aClassSymbol. self assert: ((Smalltalk at: aClassSymbol) includesSelector: aMethodSymbol)! ! !GoferOperationTest methodsFor: 'utilities' stamp: 'lr 8/20/2009 21:03'! compile: aClassSelector method: aString self assertClass: aClassSelector. (Smalltalk at: aClassSelector) compile: aString.! ! !GoferOperationTest methodsFor: 'utilities' stamp: 'lr 8/20/2009 21:04'! evaluate: aClassSelector selector: aMethodSelector self assertClass: aClassSelector selector: aMethodSelector. ^ (Smalltalk at: aClassSelector) new perform: aMethodSelector! ! !GoferOperationTest methodsFor: 'utilities' stamp: 'lr 8/20/2009 21:14'! hasPackage: aString | package | package := MCWorkingCopy allManagers detect: [ :each | each packageName = aString ] ifNone: [ nil ]. ^ package notNil! ! !GoferOperationTest methodsFor: 'running' stamp: 'lr 12/13/2009 15:56'! setUp gofer := Gofer new. gofer repository: self monticelloRepository. gofer package: 'Gofer-Foo'; package: 'Gofer-Bar'! ! !GoferOperationTest methodsFor: 'running' stamp: 'lr 8/20/2009 21:12'! tearDown [ gofer unload ] on: Error do: [ :err | "assume it is not there" ]! ! !GoferOperationTest methodsFor: 'testing' stamp: 'lr 12/13/2009 15:56'! testLoad self shouldnt: [ gofer load ] raise: Error. self assert: (self hasPackage: 'Gofer-Foo'). self assert: (self hasPackage: 'Gofer-Bar')! ! !GoferOperationTest methodsFor: 'testing' stamp: 'lr 12/13/2009 15:57'! testRecompile self shouldnt: [ gofer recompile ] raise: Error. self assert: (self hasPackage: 'Gofer-Foo'). self assert: (self hasPackage: 'Gofer-Bar')! ! !GoferOperationTest methodsFor: 'testing' stamp: 'lr 12/13/2009 15:57'! testUnload gofer load. self shouldnt: [ gofer unload ] raise: Error. self deny: (self hasPackage: 'Gofer-Foo'). self deny: (self hasPackage: 'Gofer-Bar')! ! GoferTest subclass: #GoferReferenceTest instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'Gofer-Test'! !GoferReferenceTest methodsFor: 'testing-reference' stamp: 'lr 12/13/2009 13:27'! testContraintShouldFindLatestVersion | constraintReference reference | constraintReference := GoferConstraintReference name: 'Gofer-Bar' constraint: [ :ref | true ]. self assert: (constraintReference resolveAllWith: self repositoryConfiguration) size = 4. reference := constraintReference resolveWith: self repositoryConfiguration. self assert: reference package = 'Gofer-Bar'. self assert: reference author = 'lr'. self assert: reference branch isEmpty. self assert: reference version = 1. self assert: reference repository = self monticelloRepository. constraintReference := GoferConstraintReference name: 'Gofer-Bar' constraint: [ :ref | ref branch = 'branch' ]. self assert: (constraintReference resolveAllWith: self repositoryConfiguration) size = 2. reference := constraintReference resolveWith: self repositoryConfiguration. self assert: reference package = 'Gofer-Bar'. self assert: reference author = 'lr'. self assert: reference branch = 'branch'. self assert: reference version = 2. self assert: reference repository = self monticelloRepository. constraintReference := GoferConstraintReference name: 'Gofer-Bar' constraint: [ :ref | ref author = 'jf' ]. self assert: (constraintReference resolveAllWith: self repositoryConfiguration) size = 1. reference := constraintReference resolveWith: self repositoryConfiguration. self assert: reference package = 'Gofer-Bar'. self assert: reference author = 'jf'. self assert: reference branch isEmpty. self assert: reference version = 1. self assert: reference repository = self monticelloRepository. constraintReference := GoferConstraintReference name: 'Gofer-Bar' constraint: [ :ref | false ]. self assert: (constraintReference resolveAllWith: self repositoryConfiguration) isEmpty. self should: [ constraintReference resolveWith: self repositoryConfiguration ] raise: Error.! ! !GoferReferenceTest methodsFor: 'testing-working' stamp: 'lr 12/12/2009 00:21'! testContraintShouldFindWorkingCopy | constraintReference workingCopy | constraintReference := GoferConstraintReference name: 'Gofer' constraint: [ :reference | false ]. workingCopy := constraintReference workingCopy. self assert: workingCopy packageName = 'Gofer'! ! !GoferReferenceTest methodsFor: 'testing' stamp: 'lr 12/13/2009 13:27'! testLoadableShouldSortCorrectly | sorted | sorted := self versionReferences collect: [ :each | each resolveWith: self repositoryConfiguration ]. sorted withIndexDo: [ :first :firstIndex | sorted withIndexDo: [ :second :secondIndex | firstIndex <= secondIndex ifTrue: [ self assert: first <= second ]. firstIndex >= secondIndex ifTrue: [ self assert: second <= first ] ] ]! ! !GoferReferenceTest methodsFor: 'testing-reference' stamp: 'lr 12/13/2009 13:27'! testPackageShouldFindLatestVersion | packageReference reference | packageReference := GoferPackageReference name: 'Gofer-Foo'. reference := packageReference resolveWith: self repositoryConfiguration. self assert: reference package = 'Gofer-Foo'. self assert: reference author = 'lr'. self assert: reference branch isEmpty. self assert: reference version = 4. self assert: reference repository = self monticelloRepository! ! !GoferReferenceTest methodsFor: 'testing-working' stamp: 'lr 10/2/2009 09:48'! testPackageShouldFindWorkingCopy | packageReference workingCopy | packageReference := GoferPackageReference name: 'Gofer'. workingCopy := packageReference workingCopy. self assert: workingCopy packageName = 'Gofer'! ! !GoferReferenceTest methodsFor: 'testing-reference' stamp: 'lr 12/13/2009 13:27'! testResolvedShouldFindLatestVersion | versionReference reference | versionReference := GoferResolvedReference name: 'Gofer-Foo-lr.2' repository: self monticelloRepository. reference := versionReference resolveWith: self repositoryConfiguration. self assert: reference package = 'Gofer-Foo'. self assert: reference author = 'lr'. self assert: reference branch isEmpty. self assert: reference version = 2. self assert: reference repository = self monticelloRepository! ! !GoferReferenceTest methodsFor: 'testing-working' stamp: 'TestRunner 12/12/2009 11:13'! testResolvedShouldFindWorkingCopy | versionReference workingCopy | versionReference := GoferResolvedReference name: 'Gofer-lr.18' repository: self monticelloRepository. workingCopy := versionReference workingCopy. self assert: workingCopy packageName = 'Gofer'! ! !GoferReferenceTest methodsFor: 'testing-reference' stamp: 'lr 12/13/2009 13:27'! testVersionShouldFindLatestVersion | versionReference reference | versionReference := GoferVersionReference name: 'Gofer-Foo-lr.2'. reference := versionReference resolveWith: self repositoryConfiguration. self assert: reference package = 'Gofer-Foo'. self assert: reference author = 'lr'. self assert: reference version = 2. self assert: reference branch isEmpty. self assert: reference repository = self monticelloRepository. versionReference := GoferVersionReference name: 'Gofer-Foo-lr.3'. self should: [ versionReference resolveWith: self repositoryConfiguration ] raise: Error! ! !GoferReferenceTest methodsFor: 'testing-working' stamp: 'lr 12/12/2009 00:27'! testVersionShouldFindWorkingCopy | versionReference workingCopy | versionReference := GoferVersionReference name: 'Gofer-lr.18'. workingCopy := versionReference workingCopy. self assert: workingCopy packageName = 'Gofer'! ! !GoferReferenceTest methodsFor: 'testing' stamp: 'lr 12/12/2009 00:31'! testVersionShouldParseComplexName | queryReference | queryReference := GoferVersionReference name: 'Seaside2.8b5'. self assert: queryReference package = 'Seaside2.8b5'. self assert: queryReference author isEmpty. self assert: queryReference branch isEmpty. self assert: queryReference version = 0. queryReference := GoferVersionReference name: 'Seaside2.8b5-avi.1'. self assert: queryReference package = 'Seaside2.8b5'. self assert: queryReference author = 'avi'. self assert: queryReference branch isEmpty. self assert: queryReference version = 1. queryReference := GoferVersionReference name: 'Seaside-Core-pmm.2'. self assert: queryReference package = 'Seaside-Core'. self assert: queryReference author = 'pmm'. self assert: queryReference branch isEmpty. self assert: queryReference version = 2. queryReference := GoferVersionReference name: 'Seaside-Core-jf.configcleanup.3'. self assert: queryReference package = 'Seaside-Core'. self assert: queryReference author = 'jf'. self assert: queryReference branch = 'configcleanup'. self assert: queryReference version = 3. queryReference := GoferVersionReference name: 'Seaside-Core-lr.configcleanup.extraspeedup.69'. self assert: queryReference package = 'Seaside-Core'. self assert: queryReference author = 'lr'. self assert: queryReference branch = 'configcleanup.extraspeedup'. self assert: queryReference version = 69. queryReference := GoferVersionReference name: 'Seaside-Core-lr.configcleanup42.extraspeedup.69'. self assert: queryReference package = 'Seaside-Core'. self assert: queryReference author = 'lr'. self assert: queryReference branch = 'configcleanup42.extraspeedup'. self assert: queryReference version = 69 ! ! !GoferTest class methodsFor: 'testing' stamp: 'lr 10/1/2009 22:00'! isAbstract ^ self name = #GoferTest! ! !GoferTest class methodsFor: 'accessing' stamp: 'lr 10/1/2009 21:53'! packageNamesUnderTest ^ #('Gofer')! ! !GoferTest class methodsFor: 'accessing' stamp: 'lr 12/11/2009 23:54'! resources ^ Array with: GoferResource! ! !GoferTest methodsFor: 'accessing' stamp: 'lr 12/12/2009 10:44'! monticelloRepository ^ GoferResource current monticelloRepository! ! !GoferTest methodsFor: 'accessing' stamp: 'lr 12/13/2009 13:27'! repositoryConfiguration ^ GoferResource current repositoryConfiguration! ! !GoferTest methodsFor: 'accessing' stamp: 'lr 12/12/2009 10:46'! versionReferences ^ GoferResource current versionReferences! ! !MCDictionaryRepository methodsFor: '*gofer-accessing' stamp: 'lr 12/11/2009 22:31'! goferPriority ^ 10! ! !MCDictionaryRepository methodsFor: '*gofer-accessing' stamp: 'TestRunner 12/12/2009 11:12'! goferReferences ^ self allVersionInfos collect: [ :each | GoferResolvedReference name: each name repository: self ]! ! !MCDictionaryRepository methodsFor: '*gofer-accessing' stamp: 'TestRunner 12/13/2009 14:57'! goferVersionFrom: aVersionReference ^ self dictionary detect: [ :version | version info name = aVersionReference name ]! ! Object subclass: #Gofer instanceVariableNames: 'references repository' classVariableNames: '' poolDictionaries: '' category: 'Gofer-Core'! !Gofer commentStamp: 'lr 12/3/2009 21:06' prior: 0! : Gofer, a person who runs errands. Origin 1960s: from go for, i.e. go and fetch. : ''The New Oxford American Dictionary'' !! Synopsis Gofer is a small tool on top of Monticello that loads, updates, merges, diffs, reverts, commits, recompiles and unloads groups of Monticello packages. Contrary to existing tools Gofer makes sure that these operations are performed as clean as possible: - Gofer treats packages from one or more repository in one operation. - Gofer works with fixed versions or tries to find the "latest" version using a given name prefix. - Gofer automatically assigns repositories to all packages, so that the other tools are ready to be used on individual packages. - Gofer makes sure that there is only one repository instance registered for a single physical location. - Gofer works with Monticello dependencies and uniformly treats them like the primary package. - Gofer cleans up after Monticello, no empty class categories and no empty method protocols are to be expected. !! Installation Gofer is included with the latest Pharo and GemStone distributions. To update to the latest version you can use Gofer itself: == Gofer gofer update In case you are missing Gofer in your image, grab it from *http://source.lukas-renggli.ch/gofer.html*. !! Description Gofer is very simple by design, the basic useage scenario is always the same and consists of three steps: # You specify a Monticello repository URL. You can do this using the methods ==url:== or ==url:username:password:== if you need full control, or using convenience methods like ==squeaksource:==, ==wiresong:==, or ==gemsource:== for well known repositories. # You specify one or more Monticello packages you want to work with, by adding them to the Gofer instance. Use ==addVersion:== to add a specific version, or use ==addPackage:== to add the "latest" version in the given repository. Furthermore there is ==addPackage:constraint:== that allows to further constraint the version to be loaded in a block passed in as the second argument. # You specify one or more actions to be performed on the specified packages: | ==load== | Load the specified packages. | ==update== | Update the specified packages. | ==merge== | Merge the specified packages into their working copies. | ==changes== | Display the changes between the specified versions and their working copy. | ==log== | Answer a change log between the specified package versions and the working copy. | ==cleanup== | Cleans the specified packages. | ==commit== | Commit the modified specified packages. | ==commit:== | Commit the modified specified packages with the given commit message. | ==revert== | Revert the specified packages to the currently loaded version. | ==recompile== | Recompile the specified packages. | ==unload== | Unload the specified packages. | ==fetch== | Download versions from remote repositories into the local cache. | ==push== | Upload local versions from local cache into remote repositories. !! Example To use Gofer to load exact versions of the Kom Server, the 'latest' code of Seaside 2.8 and the 'latest' code of the Scriptaculous package that is committed by the author with the initials 'lr' one could write: == Gofer new == squeaksource: 'KomHttpServer'; == addVersion: 'DynamicBindings-gc.7'; == addVersion: 'KomServices-gc.19'; == addVersion: 'KomHttpServer-gc.32'; == squeaksource: 'Seaside'; == addPackage: 'Seaside2.8a'; == addPackage: 'Scriptaculous' constraint: [ :version | version author = 'lr' ]; == load! !Gofer class methodsFor: 'examples' stamp: 'lr 12/12/2009 14:49'! gofer "Create a Gofer instance of Gofer." ^ self new renggli: 'gofer'; package: 'Gofer'; yourself! ! !Gofer class methodsFor: 'instance creation' stamp: 'lr 11/6/2009 10:50'! it ^ self new! ! !Gofer class methodsFor: 'examples' stamp: 'lr 12/12/2009 14:50'! komanche "Create a Gofer instance of Komanche." ^ self new squeaksource: 'KomHttpServer'; package: 'DynamicBindings'; package: 'KomServices'; package: 'KomHttpServer'; yourself! ! !Gofer class methodsFor: 'examples' stamp: 'lr 12/12/2009 14:50'! magritte "Create a Gofer instance of Magritte." ^ self new renggli: 'magritte'; package: 'Magritte-Model'; package: 'Magritte-Tests'; package: 'Magritte-Seaside'; package: 'Magritte-Morph'; yourself! ! !Gofer class methodsFor: 'instance creation' stamp: 'lr 8/20/2009 09:54'! new ^ self basicNew initialize! ! !Gofer class methodsFor: 'examples' stamp: 'lr 12/12/2009 14:50'! omnibrowser "Create a Gofer instance of OmniBrowser." ^ self new renggli: 'omnibrowser'; package: 'OmniBrowser'; package: 'OB-Standard'; package: 'OB-Morphic'; package: 'OB-Shout'; package: 'OB-Refactory'; package: 'OB-Regex'; package: 'OB-SUnitIntegration'; yourself! ! !Gofer class methodsFor: 'examples' stamp: 'lr 12/12/2009 14:50'! pier "Create a Gofer instance of Pier." ^ self new renggli: 'pier'; package: 'Pier-Model'; package: 'Pier-Tests'; package: 'Pier-Seaside'; package: 'Pier-Blog'; package: 'Pier-Security'; package: 'Pier-Squeak-Persistency'; yourself! ! !Gofer class methodsFor: 'examples' stamp: 'lr 12/12/2009 14:50'! pierAddons "Create a Gofer instance of Pier Addons." ^ self new renggli: 'pieraddons'; package: 'Pier-Design'; package: 'Pier-Documents'; package: 'Pier-EditorEnh'; package: 'Pier-Google'; package: 'Pier-Links'; package: 'Pier-Randomizer'; package: 'Pier-TagCloud'; package: 'Pier-Slideshow'; package: 'Pier-Setup'; yourself! ! !Gofer class methodsFor: 'examples' stamp: 'lr 12/12/2009 14:50'! refactoring "Create a Gofer instance of the refactoring tools." ^ self new squeaksource: 'rb'; package: 'AST-Core'; package: 'AST-Tests'; package: 'Refactoring-Core'; package: 'Refactoring-Tests'; package: 'Refactoring-Spelling'; yourself! ! !Gofer class methodsFor: 'examples' stamp: 'lr 12/12/2009 14:50'! seaside28 "Create a Gofer instance of Seaside 2.8." ^ self new squeaksource: 'Seaside'; squeaksource: 'rsrss'; package: 'Seaside2.8a1'; package: 'Scriptaculous'; package: 'Comet'; package: 'RSRSS2'; yourself! ! !Gofer class methodsFor: 'examples' stamp: 'lr 12/12/2009 14:51'! tools "Create a Gofer instance of several development tools." ^ self new renggli: 'unsorted'; package: 'Shout'; package: 'ShoutWorkspace'; package: 'RoelTyper'; package: 'ECompletion'; package: 'ECompletionOmniBrowser'; yourself! ! !Gofer methodsFor: 'deprecated' stamp: 'lr 12/9/2009 22:51'! addPackage: aString self package: aString! ! !Gofer methodsFor: 'deprecated' stamp: 'lr 12/9/2009 22:51'! addPackage: aString constraint: aOneArgumentBlock self package: aString constraint: aOneArgumentBlock! ! !Gofer methodsFor: 'deprecated' stamp: 'lr 12/9/2009 22:51'! addVersion: aString self version: aString! ! !Gofer methodsFor: 'operations' stamp: 'lr 10/3/2009 11:31'! cleanup "Cleans the specified packages." ^ self execute: GoferCleanup! ! !Gofer methodsFor: 'operations' stamp: 'lr 11/10/2009 10:08'! commit "Commit the modified packages." ^ self execute: GoferCommit! ! !Gofer methodsFor: 'operations' stamp: 'lr 11/10/2009 10:08'! commit: aString "Commit the modified packages with the given commit message." ^ self execute: GoferCommit do: [ :operation | operation message: aString ]! ! !Gofer methodsFor: 'repositories-convenience' stamp: 'lr 7/10/2009 16:27'! croquet: aString self url: 'http://hedgehog.software.umn.edu:8888/' , aString! ! !Gofer methodsFor: 'repositories' stamp: 'lr 12/9/2009 22:16'! directory: aDirectoryOrString "Add aDirectory as a local-repository for the following package operations." self repository addDirectory: aDirectoryOrString! ! !Gofer methodsFor: 'private' stamp: 'lr 10/2/2009 10:11'! execute: anOperationClass ^ self execute: anOperationClass do: nil! ! !Gofer methodsFor: 'private' stamp: 'lr 12/13/2009 14:04'! execute: anOperationClass do: aBlock | operation result | operation := anOperationClass on: self copy. aBlock isNil ifFalse: [ aBlock value: operation ]. ^ [ operation execute ] ensure: [ repository flush ]! ! !Gofer methodsFor: 'operations' stamp: 'lr 12/3/2009 21:06'! fetch "Download versions from remote repositories into the local cache." ^ self execute: GoferFetch! ! !Gofer methodsFor: 'repositories-convenience' stamp: 'dkh 10/16/2009 10:04'! gemsource: aString self url: 'http://seaside.gemstone.com/ss/' , aString! ! !Gofer methodsFor: 'repositories-convenience' stamp: 'lr 7/10/2009 16:27'! impara: aString self url: 'http://source.impara.de/' , aString! ! !Gofer methodsFor: 'initialization' stamp: 'TestRunner 12/13/2009 15:52'! initialize references := OrderedCollection new. repository := GoferRepositoryConfiguration new! ! !Gofer methodsFor: 'operations' stamp: 'lr 11/30/2009 14:17'! load "Load the specified packages into the image." ^ self execute: GoferLoad! ! !Gofer methodsFor: 'operations' stamp: 'lr 12/12/2009 12:50'! localChanges "Display the changes between the base and the working copy." ^ self execute: GoferLocalChanges! ! !Gofer methodsFor: 'operations' stamp: 'lr 11/10/2009 10:10'! log "Answer a change log between the specified package versions and the working copy." ^ self execute: GoferLog! ! !Gofer methodsFor: 'operations' stamp: 'lr 11/10/2009 10:06'! merge "Merge the specified packages into their working copies." ^ self execute: GoferMerge! ! !Gofer methodsFor: 'adding' stamp: 'lr 12/13/2009 13:25'! package: aString "Add the package aString to the receiver." references addLast: (GoferPackageReference name: aString)! ! !Gofer methodsFor: 'adding' stamp: 'lr 12/13/2009 13:25'! package: aString constraint: aOneArgumentBlock "Add the package aString to the receiver, constraint the resulting versions further with aOneArgumentBlock." references addLast: (GoferConstraintReference name: aString constraint: aOneArgumentBlock)! ! !Gofer methodsFor: 'copying' stamp: 'lr 12/13/2009 13:24'! postCopy references := references copy. repository := repository copy! ! !Gofer methodsFor: 'operations' stamp: 'lr 12/3/2009 21:06'! push "Upload local versions from local cache into remote repositories." ^ self execute: GoferPush! ! !Gofer methodsFor: 'operations' stamp: 'lr 8/20/2009 11:44'! recompile "Recompile the specified packages." ^ self execute: GoferRecompile! ! !Gofer methodsFor: 'accessing' stamp: 'lr 12/13/2009 13:24'! references "Answer the reference collection of the receiver." ^ references! ! !Gofer methodsFor: 'operations' stamp: 'lr 12/12/2009 12:49'! remoteChanges "Display the changes between the working copy and the remote changes." ^ self execute: GoferRemoteChanges! ! !Gofer methodsFor: 'repositories-convenience' stamp: 'lr 7/10/2009 16:25'! renggli: aString self url: 'http://source.lukas-renggli.ch/' , aString! ! !Gofer methodsFor: 'accessing' stamp: 'lr 12/13/2009 13:25'! repository "Answer the repository configuration of the receiver." ^ repository! ! !Gofer methodsFor: 'repositories' stamp: 'lr 12/9/2009 22:17'! repository: aRepository "Add aRepository as a repository for the following package operations." self repository addRepository: aRepository! ! !Gofer methodsFor: 'querying' stamp: 'lr 12/13/2009 13:55'! resolved "Answer the resolved references of the receiver." ^ self references collect: [ :reference | reference resolveWith: self repository ]! ! !Gofer methodsFor: 'operations' stamp: 'lr 8/20/2009 10:15'! revert "Revert the specified packages to the currently loaded version." ^ self execute: GoferRevert! ! !Gofer methodsFor: 'repositories-convenience' stamp: 'lr 7/10/2009 16:29'! saltypickle: aString self url: 'http://squeak.saltypickle.com/' , aString! ! !Gofer methodsFor: 'repositories-convenience' stamp: 'lr 7/10/2009 16:28'! squeakfoundation: aString self url: 'http://source.squeakfoundation.org/' , aString! ! !Gofer methodsFor: 'repositories-convenience' stamp: 'lr 7/10/2009 16:28'! squeaksource: aString self url: 'http://www.squeaksource.com/' , aString! ! !Gofer methodsFor: 'operations' stamp: 'lr 11/10/2009 10:07'! unload "Unload the specified packages." ^ self execute: GoferUnload! ! !Gofer methodsFor: 'operations' stamp: 'lr 9/18/2009 18:12'! update "Update the specified packages." ^ self execute: GoferUpdate! ! !Gofer methodsFor: 'repositories' stamp: 'lr 12/9/2009 22:17'! url: anUrlString "Add anUrlString as a repository for the following package operations." self url: anUrlString username: String new password: String new! ! !Gofer methodsFor: 'repositories' stamp: 'lr 12/9/2009 22:18'! url: anUrlString username: aUsernameString password: aPasswordString "Add anUrlString as a repository for the following package operations." self repository addUrl: anUrlString username: aUsernameString password: aPasswordString! ! !Gofer methodsFor: 'adding' stamp: 'lr 12/13/2009 13:25'! version: aString "Add the version aString to the receiver." references addLast: (GoferVersionReference name: aString)! ! !Gofer methodsFor: 'repositories-convenience' stamp: 'lr 7/10/2009 16:26'! wiresong: aString self url: 'http://source.wiresong.ca/' , aString! ! Object subclass: #GoferOperation instanceVariableNames: 'gofer model' classVariableNames: '' poolDictionaries: '' category: 'Gofer-Core'! GoferOperation subclass: #GoferLoad instanceVariableNames: 'versions' classVariableNames: '' poolDictionaries: '' category: 'Gofer-Core'! !GoferLoad methodsFor: 'private' stamp: 'TestRunner 12/13/2009 14:58'! addResolved: aResolvedReference | version | version := aResolvedReference loadableVersion. version withAllDependenciesDo: [ :dependency | versions addLast: dependency ]. model addVersion: version! ! !GoferLoad methodsFor: 'private' stamp: 'lr 9/3/2009 11:00'! defaultModel ^ MCVersionLoader new! ! !GoferLoad methodsFor: 'running' stamp: 'dkh 10/12/2009 12:56'! execute self model goferHasVersions ifTrue: [ self model load ]. self updateRepositories. self updateCategories! ! !GoferLoad methodsFor: 'initialization' stamp: 'lr 12/12/2009 11:30'! initialize super initialize. versions := OrderedCollection new! ! !GoferLoad methodsFor: 'initialization' stamp: 'TestRunner 12/13/2009 14:49'! initializeOn: aGofer super initializeOn: aGofer. aGofer resolved do: [ :each | self addResolved: each ] displayingProgress: 'Loading Versions'! ! !GoferLoad methodsFor: 'private' stamp: 'lr 9/20/2009 13:43'! updateCategories "This method makes sure that the categories are ordered in load-order and as specified in the packages." | categories | categories := OrderedCollection new. versions do: [ :version | version snapshot definitions do: [ :definition | definition isOrganizationDefinition ifTrue: [ definition categories do: [ :category | (categories includes: category) ifFalse: [ categories addLast: category ] ] ] ] ]. (MCOrganizationDefinition categories: categories) postloadOver: nil! ! !GoferLoad methodsFor: 'private' stamp: 'lr 12/12/2009 11:33'! updateRepositories "This code makes sure that all packages have a repository assigned, including the dependencies." versions do: [ :version | gofer repository do: [ :repository | version workingCopy repositoryGroup addRepository: repository ] ]! ! !GoferOperation class methodsFor: 'instance creation' stamp: 'TestRunner 12/12/2009 11:09'! new self error: 'Gofer operations can only work on Gofer instances.'! ! !GoferOperation class methodsFor: 'instance creation' stamp: 'lr 8/20/2009 12:01'! on: aGofer ^ self basicNew initializeOn: aGofer! ! !GoferOperation methodsFor: 'private' stamp: 'lr 8/19/2009 14:01'! defaultModel ^ nil! ! !GoferOperation methodsFor: 'running' stamp: 'lr 8/17/2009 14:40'! execute "Execute the receiving action." self subclassResponsibility! ! !GoferOperation methodsFor: 'accessing' stamp: 'lr 10/3/2009 11:38'! gofer "Answer the Gofer instance that triggered this operation." ^ gofer! ! !GoferOperation methodsFor: 'initialization' stamp: 'lr 8/19/2009 14:01'! initialize model := self defaultModel! ! !GoferOperation methodsFor: 'initialization' stamp: 'TestRunner 12/12/2009 11:09'! initializeOn: aGofer gofer := aGofer. self initialize! ! !GoferOperation methodsFor: 'accessing' stamp: 'lr 8/20/2009 10:13'! model "Answer the Monticello model of this operation." ^ model! ! GoferOperation subclass: #GoferSynchronize instanceVariableNames: 'references repository' classVariableNames: '' poolDictionaries: '' category: 'Gofer-Core'! GoferSynchronize subclass: #GoferFetch instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'Gofer-Core'! !GoferFetch methodsFor: 'private' stamp: 'lr 11/30/2009 13:46'! defaultModel ^ Set new! ! !GoferFetch methodsFor: 'running' stamp: 'lr 12/12/2009 14:37'! execute self model do: [ :reference | self cacheRepository storeVersion: reference loadableVersion ] displayingProgress: 'Fetching Versions'! ! !GoferFetch methodsFor: 'initialization' stamp: 'lr 12/12/2009 14:39'! initializeOn: aGofer super initializeOn: aGofer. self gofer references do: [ :reference | repository references reverseDo: [ :resolved | ((reference matches: resolved) and: [ (references includes: resolved) not ]) ifTrue: [ self model add: resolved ] ] ]! ! GoferSynchronize subclass: #GoferPush instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'Gofer-Core'! !GoferPush methodsFor: 'private' stamp: 'lr 11/30/2009 13:46'! defaultModel ^ OrderedCollection new! ! !GoferPush methodsFor: 'running' stamp: 'lr 12/12/2009 14:47'! execute self model do: [ :assocation | assocation value storeVersion: assocation key loadableVersion ] displayingProgress: 'Pushing Versions'! ! !GoferPush methodsFor: 'initialization' stamp: 'lr 12/12/2009 14:46'! initializeOn: aGofer super initializeOn: aGofer. self gofer references do: [ :reference | references do: [ :resolved | (reference matches: resolved) ifTrue: [ repository do: [ :monticelloRepository | ((repository referencesIn: monticelloRepository) includes: resolved) ifFalse: [ self model add: resolved -> monticelloRepository ] ] ] ] ]! ! !GoferSynchronize methodsFor: 'accessing' stamp: 'lr 12/12/2009 14:29'! cacheRepository ^ MCCacheRepository default! ! !GoferSynchronize methodsFor: 'initialization' stamp: 'lr 12/12/2009 14:38'! initializeOn: aGofer super initializeOn: aGofer. MCFileBasedRepository flushAllCaches. references := self gofer repository referencesIn: self cacheRepository. repository := aGofer repository copy. repository disableCache! ! GoferOperation subclass: #GoferWorking instanceVariableNames: 'workingCopies' classVariableNames: '' poolDictionaries: '' category: 'Gofer-Core'! GoferWorking subclass: #GoferChanges instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'Gofer-Core'! !GoferChanges methodsFor: 'private' stamp: 'lr 12/12/2009 12:56'! addReference: aReference super addReference: aReference. self model operations addAll: (self patchsetOf: aReference) operations! ! !GoferChanges methodsFor: 'private' stamp: 'lr 8/19/2009 14:02'! defaultModel ^ MCPatch operations: OrderedCollection new! ! !GoferChanges methodsFor: 'running' stamp: 'lr 8/19/2009 14:06'! execute self model isEmpty ifFalse: [ self model browse ]! ! !GoferChanges methodsFor: 'queries' stamp: 'lr 12/12/2009 13:06'! patchsetOf: aReference "Answer the source snapshot of aReference." | source target | source := self sourceSnapshotOf: aReference. target := self targetSnapshotOf: aReference. ^ target patchRelativeToBase: source! ! !GoferChanges methodsFor: 'queries' stamp: 'lr 12/12/2009 13:00'! sourceSnapshotOf: aReference "Answer the source snapshot of aReference." self subclassResponsibility! ! !GoferChanges methodsFor: 'queries' stamp: 'lr 12/12/2009 12:59'! targetSnapshotOf: aReference "Answer the source snapshot of aReference." self subclassResponsibility! ! GoferChanges subclass: #GoferLocalChanges instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'Gofer-Core'! !GoferLocalChanges methodsFor: 'queries' stamp: 'lr 12/12/2009 13:05'! sourceSnapshotOf: aReference | ancestors reference | ancestors := aReference workingCopy ancestry ancestors. ancestors isEmpty ifTrue: [ ^ MCSnapshot new ]. reference := GoferVersionReference name: ancestors first name. ^ (reference resolveWith: self gofer repository) loadableVersion snapshot! ! !GoferLocalChanges methodsFor: 'queries' stamp: 'lr 12/12/2009 13:01'! targetSnapshotOf: aReference ^ aReference workingCopy package snapshot! ! GoferChanges subclass: #GoferRemoteChanges instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'Gofer-Core'! !GoferRemoteChanges methodsFor: 'queries' stamp: 'lr 12/12/2009 13:00'! sourceSnapshotOf: aReference ^ aReference workingCopy package snapshot! ! !GoferRemoteChanges methodsFor: 'private' stamp: 'lr 12/12/2009 12:58'! targetSnapshotOf: aReference ^ (aReference resolveWith: self gofer repository) loadableVersion snapshot! ! GoferWorking subclass: #GoferCleanup instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'Gofer-Core'! !GoferCleanup methodsFor: 'cleaning' stamp: 'lr 10/3/2009 11:37'! cleanup: aWorkingCopy self cleanupCategories: aWorkingCopy. self cleanupProtocols: aWorkingCopy! ! !GoferCleanup methodsFor: 'cleaning' stamp: 'dkh 10/12/2009 12:59'! cleanupCategories: aWorkingCopy aWorkingCopy packageInfo systemCategories do: [ :category | (SystemOrganization goferClassesInCategory: category) isEmpty ifTrue: [ SystemOrganization removeSystemCategory: category ] ]! ! !GoferCleanup methodsFor: 'cleaning' stamp: 'lr 10/3/2009 11:37'! cleanupProtocols: aWorkingCopy aWorkingCopy packageInfo extensionClasses do: [ :class | (aWorkingCopy packageInfo extensionCategoriesForClass: class) do: [ :category | (class organization listAtCategoryNamed: category) isEmpty ifTrue: [ class organization removeCategory: category ] ] ]. aWorkingCopy packageInfo classesAndMetaClasses do: [ :class | (aWorkingCopy packageInfo coreCategoriesForClass: class) do: [ :category | (class organization listAtCategoryNamed: category) isEmpty ifTrue: [ class organization removeCategory: category ] ] ]! ! !GoferCleanup methodsFor: 'running' stamp: 'lr 10/3/2009 11:30'! execute self workingCopies do: [ :each | self cleanup: each ]! ! GoferWorking subclass: #GoferCommit instanceVariableNames: 'repositories message' classVariableNames: '' poolDictionaries: '' category: 'Gofer-Core'! !GoferCommit methodsFor: 'private' stamp: 'lr 10/2/2009 10:05'! addReference: aPackage requiredCopy: aWorkingCopy repositories: anArray super addReference: aPackage requiredCopy: aWorkingCopy repositories: anArray. repositories at: aWorkingCopy put: anArray! ! !GoferCommit methodsFor: 'running' stamp: 'lr 9/24/2009 17:32'! execute self workingCopies do: [ :workingCopy | workingCopy needsSaving ifTrue: [ self execute: workingCopy ] ]! ! !GoferCommit methodsFor: 'running' stamp: 'lr 10/13/2009 00:29'! execute: aWorkingCopy | targets version | targets := repositories at: aWorkingCopy. targets isEmpty ifTrue: [ self error: 'No repository found for ' , aWorkingCopy packageName printString ]. version := [ aWorkingCopy newVersion ] on: MCVersionNameAndMessageRequest do: [ :notifcation | self message isNil ifTrue: [ message := notifcation outer last ]. notifcation resume: (Array with: notifcation suggestedName with: self message) ]. targets first storeVersion: version! ! !GoferCommit methodsFor: 'initialization' stamp: 'lr 8/20/2009 12:14'! initialize super initialize. repositories := Dictionary new! ! !GoferCommit methodsFor: 'accessing' stamp: 'lr 10/2/2009 10:12'! message ^ message! ! !GoferCommit methodsFor: 'accessing' stamp: 'lr 10/2/2009 10:12'! message: aString message := aString! ! GoferWorking subclass: #GoferLog instanceVariableNames: 'latestVersions' classVariableNames: '' poolDictionaries: '' category: 'Gofer-Core'! !GoferLog methodsFor: 'private' stamp: 'lr 10/22/2009 20:36'! addReference: aReference requiredCopy: aWorkingCopy repositories: anArray super addReference: aReference requiredCopy: aWorkingCopy repositories: anArray. latestVersions at: aWorkingCopy put: aReference versionReference version info! ! !GoferLog methodsFor: 'private' stamp: 'lr 11/10/2009 10:34'! allVersionsFrom: aFirstVersion to: aSecondVersion | versions result | versions := (aFirstVersion hasAncestor: aSecondVersion) ifTrue: [ Array with: aSecondVersion with: aFirstVersion ] ifFalse: [ Array with: aFirstVersion with: aSecondVersion ]. result := OrderedCollection with: versions last. versions first = versions last ifFalse: [ versions last breadthFirstAncestors do: [ :version | versions first = version ifTrue: [ ^ result ] ifFalse: [ result addLast: version ] ] ]. ^ result! ! !GoferLog methodsFor: 'running' stamp: 'lr 11/10/2009 10:34'! execute | result currentVersion | result := OrderedCollection new. latestVersions keysAndValuesDo: [ :workingCopy :targetVersion | result addAll: (self allVersionsFrom: workingCopy ancestors first to: targetVersion) ]. ^ result! ! !GoferLog methodsFor: 'initialization' stamp: 'obi 10/3/2009 20:03'! initialize super initialize. latestVersions := Dictionary new! ! GoferWorking subclass: #GoferRecompile instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'Gofer-Core'! !GoferRecompile methodsFor: 'running' stamp: 'lr 8/20/2009 11:44'! execute self workingCopies do: [ :copy | self recompile: copy ]! ! !GoferRecompile methodsFor: 'running' stamp: 'lr 8/20/2009 11:47'! recompile: aWorkingCopy aWorkingCopy packageInfo methods do: [ :each | each actualClass recompile: each methodSymbol ]! ! GoferWorking subclass: #GoferUnload instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'Gofer-Core'! !GoferUnload methodsFor: 'private' stamp: 'lr 12/12/2009 11:39'! defaultModel ^ (Smalltalk at: #MCMultiPackageLoader ifAbsent: [ :class | MCPackageLoader ]) new! ! !GoferUnload methodsFor: 'running' stamp: 'lr 10/3/2009 11:45'! execute self workingCopies do: [ :copy | self unload: copy ]. self model load. self gofer cleanup. self workingCopies do: [ :copy | self unregister: copy ]! ! !GoferUnload methodsFor: 'unloading' stamp: 'lr 10/3/2009 11:46'! unload: aWorkingCopy self unloadClasses: aWorkingCopy. self unloadPackage: aWorkingCopy ! ! !GoferUnload methodsFor: 'unloading' stamp: 'nice 10/22/2009 15:14'! unloadClasses: aWorkingCopy aWorkingCopy packageInfo classes do: [ :class | (class includesSelector: #unload) ifTrue: [ class unload ] ]! ! !GoferUnload methodsFor: 'unloading' stamp: 'lr 8/19/2009 14:00'! unloadPackage: aWorkingCopy self model unloadPackage: aWorkingCopy package! ! !GoferUnload methodsFor: 'unregistering' stamp: 'lr 8/19/2009 13:49'! unregister: aWorkingCopy self unregisterWorkingCopy: aWorkingCopy. self unregisterRepositories: aWorkingCopy. self unregisterPackageInfo: aWorkingCopy! ! !GoferUnload methodsFor: 'unregistering' stamp: 'lr 8/19/2009 13:50'! unregisterPackageInfo: aWorkingCopy PackageOrganizer default unregisterPackage: aWorkingCopy packageInfo! ! !GoferUnload methodsFor: 'unregistering' stamp: 'lr 8/19/2009 13:50'! unregisterRepositories: aWorkingCopy aWorkingCopy repositoryGroup repositories allButFirst do: [ :repository | MCWorkingCopy allManagers do: [ :copy | (copy repositoryGroup includes: repository) ifTrue: [ ^ self ] ]. MCRepositoryGroup default removeRepository: repository ]! ! !GoferUnload methodsFor: 'unregistering' stamp: 'lr 8/20/2009 11:54'! unregisterWorkingCopy: aWorkingCopy aWorkingCopy unregister! ! GoferWorking subclass: #GoferUpdate instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'Gofer-Core'! GoferUpdate subclass: #GoferMerge instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'Gofer-Core'! !GoferMerge methodsFor: 'private' stamp: 'lr 8/19/2009 14:01'! defaultModel ^ MCVersionMerger new! ! !GoferMerge methodsFor: 'running' stamp: 'lr 10/3/2009 11:39'! execute [ [ self model merge ] on: MCMergeResolutionRequest do: [ :request | request merger conflicts isEmpty ifTrue: [ request resume: true ] ifFalse: [ request pass ] ] ] valueSupplyingAnswers: #(('No Changes' true)). self gofer cleanup! ! GoferUpdate subclass: #GoferRevert instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'Gofer-Core'! !GoferRevert methodsFor: 'running' stamp: 'lr 9/19/2009 13:15'! execute self workingCopies do: [ :each | each modified: false ]. super execute! ! !GoferRevert methodsFor: 'private' stamp: 'lr 12/12/2009 13:11'! versionFor: aReference | ancestors reference | ancestors := aReference workingCopy ancestry ancestors. ancestors isEmpty ifTrue: [ ^ MCSnapshot new ]. reference := GoferVersionReference name: ancestors first name. ^ (reference resolveWith: self gofer repository) loadableVersion! ! !GoferUpdate methodsFor: 'private' stamp: 'lr 12/12/2009 13:10'! addReference: aReference super addReference: aReference. self model addVersion: (self versionFor: aReference)! ! !GoferUpdate methodsFor: 'private' stamp: 'lr 9/18/2009 18:13'! defaultModel ^ MCVersionLoader new! ! !GoferUpdate methodsFor: 'running' stamp: 'dkh 10/12/2009 12:55'! execute self model goferHasVersions ifTrue: [ self model load ]. self gofer cleanup! ! !GoferUpdate methodsFor: 'private' stamp: 'lr 12/12/2009 13:10'! versionFor: aReference ^ (aReference resolveWith: self gofer repository) loadableVersion! ! !GoferWorking methodsFor: 'private' stamp: 'lr 12/12/2009 12:56'! addReference: aReference workingCopies addLast: aReference workingCopy. aReference workingCopy requiredPackages reverseDo: [ :package | self addReference: (GoferPackageReference name: package name) ]! ! !GoferWorking methodsFor: 'initialization' stamp: 'lr 8/19/2009 13:14'! initialize super initialize. workingCopies := OrderedCollection new! ! !GoferWorking methodsFor: 'initialization' stamp: 'lr 12/12/2009 12:21'! initializeOn: aGofer super initializeOn: aGofer. aGofer references do: [ :each | self addReference: each ] separatedBy: 'Loading Versions'! ! !GoferWorking methodsFor: 'accessing' stamp: 'lr 9/24/2009 16:55'! workingCopies "Answer the working copies to be operated on." ^ workingCopies! ! Object subclass: #GoferReference instanceVariableNames: 'name' classVariableNames: '' poolDictionaries: '' category: 'Gofer-Core'! GoferReference subclass: #GoferPackageReference instanceVariableNames: 'packageName' classVariableNames: '' poolDictionaries: '' category: 'Gofer-Core'! !GoferPackageReference commentStamp: 'lr 12/9/2009 22:47' prior: 0! A GoferPackageReference refers to the latest version of a Monticello package.! GoferPackageReference subclass: #GoferConstraintReference instanceVariableNames: 'constraintBlock' classVariableNames: '' poolDictionaries: '' category: 'Gofer-Core'! !GoferConstraintReference commentStamp: 'lr 12/9/2009 22:48' prior: 0! A GoferPackageReference refers to the latest version of a Monticello package satisfying an additional set of conditions.! !GoferConstraintReference class methodsFor: 'instance creation' stamp: 'lr 12/9/2009 22:44'! name: aString constraint: aBlock ^ self basicNew initializeName: aString constraint: aBlock! ! !GoferConstraintReference methodsFor: 'initialization' stamp: 'TestRunner 12/12/2009 00:18'! initializeName: aString constraint: aBlock self initializeName: aString. constraintBlock := aBlock! ! !GoferConstraintReference methodsFor: 'private' stamp: 'lr 12/11/2009 22:14'! matches: aLoadableReference ^ (super matches: aLoadableReference) and: [ constraintBlock value: aLoadableReference ]! ! !GoferPackageReference methodsFor: 'private' stamp: 'lr 12/11/2009 22:24'! matches: aLoadableReference ^ self package = aLoadableReference package! ! !GoferPackageReference methodsFor: 'accessing' stamp: 'lr 12/11/2009 22:23'! package ^ name! ! !GoferReference class methodsFor: 'instance creation' stamp: 'lr 12/9/2009 22:42'! name: aString ^ self basicNew initializeName: aString! ! !GoferReference class methodsFor: 'instance creation' stamp: 'lr 12/9/2009 22:42'! new self error: 'Use #name: to initialize the receiver.'! ! !GoferReference methodsFor: 'comparing' stamp: 'lr 12/12/2009 13:33'! = aReference ^ self class = aReference class and: [ self name = aReference name ]! ! !GoferReference methodsFor: 'comparing' stamp: 'lr 12/12/2009 13:33'! hash ^ self name hash! ! !GoferReference methodsFor: 'initialization' stamp: 'lr 12/9/2009 22:57'! initializeName: aString name := aString! ! !GoferReference methodsFor: 'private' stamp: 'lr 12/11/2009 22:13'! matches: aLoadableReference "Answer true if the receiver matches aLoadableReference." self subclassResponsibility! ! !GoferReference methodsFor: 'accessing' stamp: 'lr 12/11/2009 22:02'! name "Answer the name of this reference." ^ name! ! !GoferReference methodsFor: 'accessing' stamp: 'lr 12/11/2009 22:24'! package "Answer the package name." self subclassResponsibility! ! !GoferReference methodsFor: 'printing' stamp: 'lr 12/11/2009 22:02'! printOn: aStream super printOn: aStream. aStream nextPutAll: ' name: '; print: self name! ! !GoferReference methodsFor: 'querying' stamp: 'lr 12/12/2009 13:44'! resolveAllWith: aGoferRepository "Answer a sorted collection of all resolved references within aGoferRepository." ^ aGoferRepository references select: [ :each | self matches: each ]! ! !GoferReference methodsFor: 'querying' stamp: 'TestRunner 12/12/2009 11:22'! resolveWith: aGoferRepository "Answer a single resolved reference within aGoferRepository, throw an error if the version can't be found.'" | references | references := self resolveAllWith: aGoferRepository. ^ references isEmpty ifTrue: [ self error: 'Unable to resolve ' , self printString , ' in ' , aGoferRepository printString ] ifFalse: [ references last ]! ! !GoferReference methodsFor: 'querying' stamp: 'TestRunner 12/12/2009 11:23'! versionWith: aGoferRepository "Answer a Monticello version of the resolved reference within aGoferRepository." ^ (self resolveWith: aGoferRepository) monticelloVersion! ! !GoferReference methodsFor: 'querying' stamp: 'lr 12/12/2009 12:22'! workingCopy "Answer a working copy or throw an error if not present." ^ MCWorkingCopy allManagers detect: [ :each | each packageName = self package ] ifNone: [ self error: 'Working copy of ' , self package printString , ' not found' ]! ! GoferReference subclass: #GoferVersionReference instanceVariableNames: 'package author branch version' classVariableNames: '' poolDictionaries: '' category: 'Gofer-Core'! !GoferVersionReference commentStamp: 'lr 12/9/2009 22:50' prior: 0! A GoferVersionReference refers to a specific version of a Monticello package.! GoferVersionReference subclass: #GoferResolvedReference instanceVariableNames: 'repository' classVariableNames: '' poolDictionaries: '' category: 'Gofer-Core'! !GoferResolvedReference commentStamp: 'lr 12/9/2009 22:53' prior: 0! A GoferVersionReference refers to a specific version of a Monticello package in a particular repository. This class is the only one that can actually access the version, because it is the only one knowing where to find it.! !GoferResolvedReference class methodsFor: 'instance creation' stamp: 'lr 12/9/2009 22:55'! name: aString repository: aRepository ^ self basicNew initializeName: aString repository: aRepository! ! !GoferResolvedReference methodsFor: 'comparing' stamp: 'TestRunner 12/12/2009 10:55'! <= aLoadableReference "Sort versions according to: 1. package name 2. branch name, list versions without branch last 3. version number 4. author name 5. repository priority" self package = aLoadableReference package ifFalse: [ ^ self package <= aLoadableReference package ]. self branch = aLoadableReference branch ifFalse: [ ^ (self branch isEmpty or: [ aLoadableReference branch isEmpty ]) ifTrue: [ self branch size > aLoadableReference branch size ] ifFalse: [ self branch <= aLoadableReference branch ] ]. self version = aLoadableReference version ifFalse: [ ^ self version <= aLoadableReference version ]. self author = aLoadableReference author ifFalse: [ ^ self author <= aLoadableReference author ]. self repository goferPriority = aLoadableReference repository goferPriority ifFalse: [ ^ self repository goferPriority > aLoadableReference repository goferPriority ]. ^ true! ! !GoferResolvedReference methodsFor: 'initialization' stamp: 'lr 12/9/2009 22:55'! initializeName: aString repository: aRepository self initializeName: aString. repository := aRepository! ! !GoferResolvedReference methodsFor: 'accessing' stamp: 'lr 12/12/2009 11:29'! loadableVersion "Answer a loadable Monticello version of the receiver." ^ self repository goferVersionFrom: self! ! !GoferResolvedReference methodsFor: 'accessing' stamp: 'lr 12/11/2009 22:33'! repository "Answer the repository of the receiver." ^ repository! ! !GoferVersionReference methodsFor: 'accessing' stamp: 'lr 12/11/2009 22:22'! author "Answer the author of the receiver." ^ author! ! !GoferVersionReference methodsFor: 'accessing' stamp: 'lr 12/11/2009 22:23'! branch "Answer the branch of the receiver." ^ branch! ! !GoferVersionReference methodsFor: 'initialization' stamp: 'lr 12/11/2009 22:17'! initializeName: aString super initializeName: aString. self parseName: aString! ! !GoferVersionReference methodsFor: 'private' stamp: 'lr 12/11/2009 22:14'! matches: aLoadableReference ^ self name = aLoadableReference name! ! !GoferVersionReference methodsFor: 'accessing' stamp: 'lr 12/11/2009 22:23'! package "Answer the package of the receiver." ^ package! ! !GoferVersionReference methodsFor: 'initialization' stamp: 'lr 12/11/2009 22:23'! parseName: aString | basicName | basicName := aString last isDigit ifTrue: [ aString ] ifFalse: [ (aString copyUpToLast: $.) copyUpTo: $( ]. package := basicName copyUpToLast: $-. author := (basicName copyAfterLast: $-) copyUpTo: $.. version := (basicName copyAfterLast: $-) copyAfter: $.. version isEmpty ifTrue: [ branch := ''. version := 0 ] ifFalse: [ (version allSatisfy: [ :each | each isDigit ]) ifTrue: [ branch := ''. version := version asInteger ] ifFalse: [ branch := version copyUpToLast: $.. version := (version copyAfterLast: $.) asInteger ] ]! ! !GoferVersionReference methodsFor: 'accessing' stamp: 'lr 12/11/2009 22:22'! version "Answer the version of the receiver." ^ version! ! Object subclass: #GoferRepositoryConfiguration instanceVariableNames: 'repositories cacheRepository errorBlock resolved' classVariableNames: '' poolDictionaries: '' category: 'Gofer-Core'! !GoferRepositoryConfiguration class methodsFor: 'instance creation' stamp: 'lr 12/9/2009 19:07'! new ^ self basicNew initialize! ! !GoferRepositoryConfiguration methodsFor: 'adding' stamp: 'lr 12/13/2009 15:51'! addDirectory: aDirectoryOrString "Add a repository as a file-system at aDirectoryOrString." | repository | repository := (aDirectoryOrString isString and: [ aDirectoryOrString endsWith: '*' ]) ifTrue: [ MCSubDirectoryRepository new directory: (FileDirectory on: aDirectoryOrString allButLast); yourself ] ifFalse: [ MCDirectoryRepository new directory: (aDirectoryOrString isString ifTrue: [ FileDirectory on: aDirectoryOrString ] ifFalse: [ aDirectoryOrString ]); yourself ]. self addRepository: repository! ! !GoferRepositoryConfiguration methodsFor: 'adding' stamp: 'lr 12/11/2009 23:02'! addRepository: aRepository "Add a repository to the repository group." MCCacheRepository default = aRepository ifTrue: [ cacheRepository := aRepository ] ifFalse: [ repositories addLast: aRepository ]! ! !GoferRepositoryConfiguration methodsFor: 'adding' stamp: 'lr 12/9/2009 22:06'! addUrl: anUrlString username: aUsernameString password: aPasswordString "Set the repository URL anUrlString as the location for the following package additions." self addRepository: ((anUrlString beginsWith: 'ftp://') ifTrue: [ MCFtpRepository host: ((anUrlString allButFirst: 6) copyUpTo: $/) directory: ((anUrlString allButFirst: 6) copyAfter: $/) user: aUsernameString password: aPasswordString ] ifFalse: [ MCHttpRepository location: anUrlString user: aUsernameString password: aPasswordString ])! ! !GoferRepositoryConfiguration methodsFor: 'private' stamp: 'lr 12/12/2009 13:43'! basicReferencesIn: aRepository ^ [ aRepository goferReferences asSortedCollection asArray ] on: GoferRepositoryError do: errorBlock! ! !GoferRepositoryConfiguration methodsFor: 'options' stamp: 'lr 12/9/2009 22:39'! disableCache "Disable the use of the package-cache repository." cacheRepository := nil! ! !GoferRepositoryConfiguration methodsFor: 'options' stamp: 'lr 12/9/2009 22:39'! disableErrors "Silently swallow all repository errors." errorBlock := [ :error | error resume: #() ]! ! !GoferRepositoryConfiguration methodsFor: 'options' stamp: 'lr 12/9/2009 22:39'! enableCache "Enable the use of the package-cache repository." cacheRepository := MCCacheRepository default! ! !GoferRepositoryConfiguration methodsFor: 'options' stamp: 'lr 12/9/2009 22:39'! enableErrors "Throw an exception when repositories are not available." errorBlock := [ :error | error pass ]! ! !GoferRepositoryConfiguration methodsFor: 'actions' stamp: 'lr 12/12/2009 14:34'! flush "Flush the version cache." resolved := Dictionary new! ! !GoferRepositoryConfiguration methodsFor: 'initialization' stamp: 'lr 12/12/2009 14:34'! initialize self reset; flush. self enableCache; enableErrors! ! !GoferRepositoryConfiguration methodsFor: 'accessing' stamp: 'TestRunner 12/13/2009 15:57'! references "Answer all the sorted references." | result | result := OrderedCollection new. self repositories do: [ :repository | result addAll: (self referencesIn: repository) ]. ^ result asSortedCollection asArray! ! !GoferRepositoryConfiguration methodsFor: 'accessing' stamp: 'lr 12/12/2009 14:34'! referencesIn: aRepository "Answer all cached and sorted references within a repository." ^ resolved at: aRepository ifAbsentPut: [ self basicReferencesIn: aRepository ]! ! !GoferRepositoryConfiguration methodsFor: 'accessing' stamp: 'TestRunner 12/13/2009 15:57'! repositories "Answer the configured monticello repositories of the receiver." repositories := OrderedCollection withAll: repositories. cacheRepository isNil ifFalse: [ repositories addFirst: cacheRepository ]. ^ repositories! ! !GoferRepositoryConfiguration methodsFor: 'actions' stamp: 'lr 12/12/2009 14:34'! reset "Reset the list of repositories." repositories := OrderedCollection new! ! !MCRepository methodsFor: '*gofer-accessing' stamp: 'lr 12/11/2009 22:31'! goferPriority ^ 0! ! !MCRepository methodsFor: '*gofer-accessing' stamp: 'lr 12/9/2009 20:50'! goferReferences ^ #()! ! !MCRepository methodsFor: '*gofer-accessing' stamp: 'lr 12/12/2009 11:29'! goferVersionFrom: aVersionReference self error: 'Unable to load from ' , self printString! ! !SystemOrganizer methodsFor: '*gofer' stamp: 'lr 12/3/2009 21:04'! goferClassesInCategory: category ^ (self listAtCategoryNamed: category) collect: [ :className | Smalltalk at: className ]! !