diff --git a/rowan/components/Deployment.ston b/rowan/components/Deployment.ston new file mode 100644 index 0000000..7e1a583 --- /dev/null +++ b/rowan/components/Deployment.ston @@ -0,0 +1,34 @@ +RwSimpleProjectLoadComponentV2 { + #name : 'Deployment', + #projectNames : [ + 'Bell', + 'Buoy', + 'INI-Parser', + 'SortFunctions' + ], + #componentNames : [ ], + #packageNames : [ + 'Launchpad-Applications', + 'Launchpad-Commands', + 'Launchpad-Configuration', + 'Launchpad-Configuration-GS64-Extensions', + 'Launchpad-GS64-Extensions', + 'Launchpad-Tracing', + 'Launchpad-Tracing-GS64' + ], + #conditionalPackageMapSpecs : { + 'gemstone' : { + 'allusers' : { + #packageNameToPlatformPropertiesMap : { + 'Launchpad-Applications' : { 'symbolDictName' : 'Launchpad' }, + 'Launchpad-Commands' : { 'symbolDictName' : 'Launchpad' }, + 'Launchpad-Configuration' : { 'symbolDictName' : 'Launchpad' }, + 'Launchpad-Configuration-GS64-Extensions' : { 'symbolDictName' : 'Globals' }, + 'Launchpad-GS64-Extensions' : { 'symbolDictName' : 'Launchpad' }, + 'Launchpad-Tracing' : { 'symbolDictName' : 'Launchpad' }, + 'Launchpad-Tracing-GS64' : { 'symbolDictName' : 'Launchpad' } + } + } + } + } +} \ No newline at end of file diff --git a/rowan/components/Tests.ston b/rowan/components/Tests.ston new file mode 100644 index 0000000..cf15595 --- /dev/null +++ b/rowan/components/Tests.ston @@ -0,0 +1,32 @@ +RwSimpleProjectLoadComponentV2 { + #name : 'Tests', + #condition : 'tests', + #projectNames : [ ], + #componentNames : [ + 'Deployment' + ], + #packageNames : [ + 'Launchpad-Applications-Tests', + 'Launchpad-Commands-Tests', + 'Launchpad-Configuration-Tests', + 'Launchpad-Examples', + 'Launchpad-GS64-Extensions-Tests', + 'Launchpad-SUnit', + 'Launchpad-Tracing-Tests' + ], + #conditionalPackageMapSpecs : { + 'gemstone' : { + 'allusers' : { + #packageNameToPlatformPropertiesMap : { + 'Launchpad-Applications-Tests' : { 'symbolDictName' : 'Launchpad' }, + 'Launchpad-Commands-Tests' : { 'symbolDictName' : 'Launchpad' }, + 'Launchpad-Configuration-Tests' : { 'symbolDictName' : 'Launchpad' }, + 'Launchpad-Examples' : { 'symbolDictName' : 'Launchpad' }, + 'Launchpad-GS64-Extensions-Tests' : { 'symbolDictName' : 'Launchpad' }, + 'Launchpad-SUnit' : { 'symbolDictName' : 'Launchpad' }, + 'Launchpad-Tracing-Tests' : { 'symbolDictName' : 'Launchpad' } + } + } + } + } +} \ No newline at end of file diff --git a/rowan/project.ston b/rowan/project.ston new file mode 100644 index 0000000..acf03b5 --- /dev/null +++ b/rowan/project.ston @@ -0,0 +1,11 @@ +RwProjectSpecificationV2 { + #specName : 'project', + #projectSpecPath : 'rowan', + #componentsPath : 'rowan/components', + #packagesPath : 'source', + #projectsPath : 'rowan/projects', + #specsPath : 'rowan/specs', + #packageFormat : 'tonel', + #packageConvention : 'Rowan', + #comment : 'Launchpad project specification' +} \ No newline at end of file diff --git a/rowan/projects/Bell.ston b/rowan/projects/Bell.ston new file mode 100644 index 0000000..cd6cd5f --- /dev/null +++ b/rowan/projects/Bell.ston @@ -0,0 +1,11 @@ +RwLoadSpecificationV2 { + #specName: 'Bell', + #projectName : 'Bell', + #gitUrl : 'https://github.com/ba-st/Bell.git', + #revision : 'v2', + #projectSpecFile : 'rowan/project.ston', + #componentNames : [ + 'Deployment', + 'Dependent-SUnit-Extensions' + ] +} diff --git a/rowan/projects/Buoy.ston b/rowan/projects/Buoy.ston new file mode 100644 index 0000000..3195a4a --- /dev/null +++ b/rowan/projects/Buoy.ston @@ -0,0 +1,11 @@ +RwLoadSpecificationV2 { + #specName: 'Buoy', + #projectName : 'Buoy', + #gitUrl : 'https://github.com/ba-st/Buoy.git', + #revision : 'v7', + #projectSpecFile : 'rowan/project.ston', + #componentNames : [ + 'Deployment', + 'Dependent-SUnit-Extensions' + ] +} diff --git a/rowan/projects/INI-Parser.ston b/rowan/projects/INI-Parser.ston new file mode 100644 index 0000000..555a91f --- /dev/null +++ b/rowan/projects/INI-Parser.ston @@ -0,0 +1,10 @@ +RwLoadSpecificationV2 { + #specName: 'INI-Parser', + #projectName : 'INI-Parser', + #gitUrl : 'https://github.com/ba-st-dependencies/INI-Parser.git', + #revision : 'v2', + #projectSpecFile : 'rowan/project.ston', + #componentNames : [ + 'Deployment' + ] +} diff --git a/rowan/projects/README.md b/rowan/projects/README.md new file mode 100644 index 0000000..e69de29 diff --git a/rowan/projects/SortFunctions.ston b/rowan/projects/SortFunctions.ston new file mode 100644 index 0000000..c008dc2 --- /dev/null +++ b/rowan/projects/SortFunctions.ston @@ -0,0 +1,10 @@ +RwLoadSpecificationV2 { + #specName: 'SortFunctions', + #projectName : 'SortFunctions', + #gitUrl : 'https://github.com/ba-st-dependencies/SortFunctions.git', + #revision : 'v1', + #projectSpecFile : 'rowan/project.ston', + #componentNames : [ + 'Deployment' + ] +} diff --git a/rowan/specs/Launchpad-CI.ston b/rowan/specs/Launchpad-CI.ston new file mode 100644 index 0000000..7e623a1 --- /dev/null +++ b/rowan/specs/Launchpad-CI.ston @@ -0,0 +1,14 @@ +RwLoadSpecificationV2 { + #specName: 'Launchpad-CI', + #projectName : 'Launchpad', + #diskUrl : 'Launchpad', + #projectSpecFile : 'rowan/project.ston', + #componentNames : [ + 'Tests' + ], + #customConditionalAttributes : [ + 'tests', + 'sunit' + ], + #comment : 'Loading spec for the continuous integration setup' +} diff --git a/rowan/specs/Launchpad-Deployment.ston b/rowan/specs/Launchpad-Deployment.ston new file mode 100644 index 0000000..3d5de10 --- /dev/null +++ b/rowan/specs/Launchpad-Deployment.ston @@ -0,0 +1,9 @@ +RwLoadSpecificationV2 { + #specName: 'Launchpad-Deployment', + #projectName : 'Launchpad', + #diskUrl : 'Launchpad', + #projectSpecFile : 'rowan/project.ston', + #componentNames : [ + 'Deployment' + ] +} diff --git a/source/.properties b/source/.properties index 53a5454..c44df9a 100644 --- a/source/.properties +++ b/source/.properties @@ -1,3 +1,4 @@ { - #format : #tonel + #format : #tonel, + #convention : 'Rowan' } diff --git a/source/BaselineOfLaunchpad/BaselineOfLaunchpad.class.st b/source/BaselineOfLaunchpad/BaselineOfLaunchpad.class.st index 70feb54..b575646 100644 --- a/source/BaselineOfLaunchpad/BaselineOfLaunchpad.class.st +++ b/source/BaselineOfLaunchpad/BaselineOfLaunchpad.class.st @@ -54,22 +54,22 @@ BaselineOfLaunchpad >> projectClass [ BaselineOfLaunchpad >> setUpDependencies: spec [ spec - baseline: 'Buoy' with: [ spec repository: 'github://ba-st/Buoy:v6' ]; + baseline: 'Buoy' with: [ spec repository: 'github://ba-st/Buoy:v7' ]; project: 'Buoy-Deployment' copyFrom: 'Buoy' with: [ spec loads: 'Deployment' ]; project: 'Buoy-SUnit' copyFrom: 'Buoy' with: [ spec loads: 'Dependent-SUnit-Extensions' ]; project: 'Buoy-Tools' copyFrom: 'Buoy' with: [ spec loads: 'Tools' ]. spec - baseline: 'Bell' with: [ spec repository: 'github://ba-st/Bell:v1' ]; + baseline: 'Bell' with: [ spec repository: 'github://ba-st/Bell:v2' ]; project: 'Bell-Deployment' copyFrom: 'Bell' with: [ spec loads: 'Deployment' ]; project: 'Bell-SUnit' copyFrom: 'Bell' with: [ spec loads: 'Dependent-SUnit-Extensions' ]. spec - baseline: 'INIParser' with: [ spec repository: 'github://ctSkennerton/INI-Parser:v1.1.1' ]; + baseline: 'INIParser' with: [ spec repository: 'github://ba-st-dependencies/INI-Parser:v2' ]; project: 'INIParser-Deployment' copyFrom: 'INIParser' with: [ spec loads: 'Deployment' ]. spec - baseline: 'Hyperspace' with: [ spec repository: 'github://ba-st/Hyperspace:v4' ]; + baseline: 'Hyperspace' with: [ spec repository: 'github://ba-st/Hyperspace:v5' ]; project: 'Hyperspace-SUnit' copyFrom: 'Hyperspace' with: [ spec loads: 'Dependent-SUnit-Extensions' ] @@ -79,21 +79,41 @@ BaselineOfLaunchpad >> setUpDependencies: spec [ BaselineOfLaunchpad >> setUpDeploymentPackages: spec [ spec - package: 'Launchpad-Configuration' - with: [ spec requires: #( 'Bell-Deployment' 'INIParser-Deployment' 'Buoy-Deployment' ) ]; + package: 'Launchpad-Configuration-Pharo-Extensions'; + group: 'Deployment' + with: 'Launchpad-Configuration-Pharo-Extensions'. + + spec + package: 'Launchpad-Configuration' with: [ + spec requires: + #( 'Bell-Deployment' 'INIParser-Deployment' + 'Buoy-Deployment' 'Launchpad-Configuration-Pharo-Extensions' ) ]; group: 'Deployment' with: 'Launchpad-Configuration'. spec - package: 'Launchpad-Applications' with: [ spec requires: 'Bell-Deployment' ]; + package: 'Launchpad-Applications' + with: [ spec requires: 'Bell-Deployment' ]; group: 'Deployment' with: 'Launchpad-Applications'. spec - package: 'Launchpad-Commands' - with: [ spec requires: #( 'Launchpad-Applications' 'Launchpad-Configuration' ) ]; + package: 'Launchpad-Commands' with: [ + spec requires: + #( 'Launchpad-Applications' 'Launchpad-Configuration' ) ]; group: 'Deployment' with: 'Launchpad-Commands'. spec - package: 'Launchpad-Tracing' with: [ spec requires: 'Launchpad-Applications' ]; + package: 'Launchpad-Commands-Pharo' + with: [ spec requires: 'Launchpad-Commands' ]; + group: 'Deployment' with: 'Launchpad-Commands-Pharo'. + + spec + package: 'Launchpad-Tracing-Pharo' + with: [ spec requires: 'Launchpad-Applications' ]; + group: 'Deployment' with: 'Launchpad-Tracing-Pharo'. + + spec + package: 'Launchpad-Tracing' + with: [ spec requires: 'Launchpad-Tracing-Pharo' ]; group: 'Deployment' with: 'Launchpad-Tracing' ] @@ -177,20 +197,37 @@ BaselineOfLaunchpad >> setUpTestPackages: spec [ spec package: 'Launchpad-Configuration-Tests' - with: [ spec requires: #( 'Launchpad-Configuration' 'Launchpad-SUnit' ) ]; + with: [ + spec requires: #( 'Launchpad-Configuration' + 'Launchpad-SUnit' ) ]; group: 'Tests' with: 'Launchpad-Configuration-Tests'. spec package: 'Launchpad-Applications-Tests' - with: [ spec requires: #( 'Launchpad-Applications' 'Launchpad-SUnit' ) ]; + with: [ + spec requires: #( 'Launchpad-Applications' 'Launchpad-SUnit' ) ]; group: 'Tests' with: 'Launchpad-Applications-Tests'. spec - package: 'Launchpad-Tracing-Tests' with: [ spec requires: 'Launchpad-Tracing' ]; + package: 'Launchpad-Tracing-Tests' + with: [ spec requires: 'Launchpad-Tracing' ]; group: 'Tests' with: 'Launchpad-Tracing-Tests'. spec - package: 'Launchpad-Commands-Tests' - with: [ spec requires: #( 'Examples' 'Launchpad-Commands' 'Launchpad-SUnit' 'Hyperspace-SUnit' ) ]; - group: 'Tests' with: 'Launchpad-Commands-Tests' + package: 'Launchpad-Tracing-Pharo-Tests' + with: [ spec requires: 'Launchpad-Tracing' ]; + group: 'Tests' with: 'Launchpad-Tracing-Pharo-Tests'. + + spec + package: 'Launchpad-Commands-Tests' with: [ + spec requires: + #( 'Examples' 'Launchpad-Commands' 'Launchpad-SUnit' + 'Hyperspace-SUnit' ) ]; + group: 'Tests' with: 'Launchpad-Commands-Tests'. + + spec + package: 'Launchpad-Commands-Pharo-Tests' with: [ + spec requires: + #( 'Launchpad-Commands-Tests' 'Launchpad-Commands-Pharo' ) ]; + group: 'Tests' with: 'Launchpad-Commands-Pharo-Tests' ] diff --git a/source/BaselineOfLaunchpad/VersionFromRepositoryResolver.class.st b/source/BaselineOfLaunchpad/VersionFromRepositoryResolver.class.st index 2520a46..f2398ca 100644 --- a/source/BaselineOfLaunchpad/VersionFromRepositoryResolver.class.st +++ b/source/BaselineOfLaunchpad/VersionFromRepositoryResolver.class.st @@ -5,16 +5,16 @@ Class { } { #category : #accessing } -VersionFromRepositoryResolver >> valueFor: aPackageName [ - - | version | +VersionFromRepositoryResolver >> valueFor: projectName [ + | packageName version | + packageName := ('BaselineOf<1s>' expandMacrosWith: projectName) asSymbol. version := ''. self class environment at: #IceRepository ifPresent: [ :icebergRepository | version := icebergRepository registry detect: [ :repository | - repository includesPackageNamed: aPackageName ] + repository includesPackageNamed: packageName ] ifFound: [ :repository | self versionFromGitRepository: repository ] - ifNone: [ self versionFromMonticelloRepository: aPackageName ] + ifNone: [ self versionFromMonticelloRepository: packageName ] ]. ^ version ] diff --git a/source/Launchpad-Applications/CurrentApplicationConfiguration.class.st b/source/Launchpad-Applications/CurrentApplicationConfiguration.class.st index 7478256..a4d89be 100644 --- a/source/Launchpad-Applications/CurrentApplicationConfiguration.class.st +++ b/source/Launchpad-Applications/CurrentApplicationConfiguration.class.st @@ -4,8 +4,14 @@ Class { #category : #'Launchpad-Applications' } +{ #category : #accessing } +CurrentApplicationConfiguration class >> default [ + + ^ LaunchpadApplication currentlyRunning configuration +] + { #category : #accessing } CurrentApplicationConfiguration >> default [ - ^LaunchpadApplication currentlyRunning configuration + ^ self class default ] diff --git a/source/Launchpad-Applications/LaunchpadApplication.class.st b/source/Launchpad-Applications/LaunchpadApplication.class.st index 0c68e9c..9625112 100644 --- a/source/Launchpad-Applications/LaunchpadApplication.class.st +++ b/source/Launchpad-Applications/LaunchpadApplication.class.st @@ -60,6 +60,12 @@ LaunchpadApplication class >> initialize [ self resetCurrentlyRunning ] +{ #category : #testing } +LaunchpadApplication class >> isAbstract [ + + ^ self = LaunchpadApplication +] + { #category : #printing } LaunchpadApplication class >> printCommandArgumentsOn: stream [ diff --git a/source/Launchpad-Applications/ReleasedApplicationMode.class.st b/source/Launchpad-Applications/ReleasedApplicationMode.class.st index 906d3e5..81c54e2 100644 --- a/source/Launchpad-Applications/ReleasedApplicationMode.class.st +++ b/source/Launchpad-Applications/ReleasedApplicationMode.class.st @@ -25,5 +25,5 @@ ReleasedApplicationMode >> isDebugMode [ { #category : #utilities } ReleasedApplicationMode >> value: aBlock onErrorDo: errorHandler [ - ^ aBlock on: Error do: errorHandler + ^ aBlock on: Error except: Exit do: errorHandler ] diff --git a/source/Launchpad-Commands-Pharo-Tests/LaunchpadRootCommandTest.extension.st b/source/Launchpad-Commands-Pharo-Tests/LaunchpadRootCommandTest.extension.st new file mode 100644 index 0000000..239fe20 --- /dev/null +++ b/source/Launchpad-Commands-Pharo-Tests/LaunchpadRootCommandTest.extension.st @@ -0,0 +1,27 @@ +Extension { #name : #LaunchpadRootCommandTest } + +{ #category : #'*Launchpad-Commands-Pharo-Tests' } +LaunchpadRootCommandTest >> testHandlingStartGreeterApplicationEnablingTCPCommandServer [ + + | output | + + output := self handle: { + 'launchpad'. + 'start'. + '--debug-mode'. + '--enable-tcp-command-server=0'. + LaunchpadGreeterApplication commandName. + '--name=John' }. + + self assert: output equals: 'Hi John!' expandMacros. + + self assertLogRecordsMatch: #( + '[INFO] Receiving commands over TCP/0' + '[INFO] greeter [v1.0.0] - A greetings application' + '[INFO] Obtaining configuration...' + '[WARNING] "Title" parameter not provided. Using default.' + '[INFO] Name: John' + '[INFO] Title: ' + '[INFO] Obtaining configuration... [DONE]' + '[INFO] Exit application' ) +] diff --git a/source/Launchpad-Commands-Tests/TCPCommandServerTest.class.st b/source/Launchpad-Commands-Pharo-Tests/TCPCommandServerTest.class.st similarity index 97% rename from source/Launchpad-Commands-Tests/TCPCommandServerTest.class.st rename to source/Launchpad-Commands-Pharo-Tests/TCPCommandServerTest.class.st index 5da7e1b..e27113c 100644 --- a/source/Launchpad-Commands-Tests/TCPCommandServerTest.class.st +++ b/source/Launchpad-Commands-Pharo-Tests/TCPCommandServerTest.class.st @@ -7,7 +7,7 @@ Class { #instVars : [ 'server' ], - #category : #'Launchpad-Commands-Tests' + #category : #'Launchpad-Commands-Pharo-Tests' } { #category : #running } diff --git a/source/Launchpad-Commands-Pharo-Tests/package.st b/source/Launchpad-Commands-Pharo-Tests/package.st new file mode 100644 index 0000000..c6af63f --- /dev/null +++ b/source/Launchpad-Commands-Pharo-Tests/package.st @@ -0,0 +1 @@ +Package { #name : #'Launchpad-Commands-Pharo-Tests' } diff --git a/source/Launchpad-Commands-Pharo/LaunchpadStartApplicationCommand.extension.st b/source/Launchpad-Commands-Pharo/LaunchpadStartApplicationCommand.extension.st new file mode 100644 index 0000000..2df8cf0 --- /dev/null +++ b/source/Launchpad-Commands-Pharo/LaunchpadStartApplicationCommand.extension.st @@ -0,0 +1,14 @@ +Extension { #name : #LaunchpadStartApplicationCommand } + +{ #category : #'*Launchpad-Commands-Pharo' } +LaunchpadStartApplicationCommand >> enableTCPCommandServerListeningOn: listeningPort [ + + commandServer := TCPCommandServer listeningOn: listeningPort. + commandServer registerCommandNamed: 'SHUTDOWN' executing: [ + [ + LaunchpadApplication currentlyRunning + stop; + exitSuccess + ] forkAt: Processor timingPriority named: 'Launchpad shutdown process' + ] +] diff --git a/source/Launchpad-Commands/LaunchpadTCPCommandServerOption.class.st b/source/Launchpad-Commands-Pharo/LaunchpadTCPCommandServerOption.class.st similarity index 90% rename from source/Launchpad-Commands/LaunchpadTCPCommandServerOption.class.st rename to source/Launchpad-Commands-Pharo/LaunchpadTCPCommandServerOption.class.st index e0f559d..38da792 100644 --- a/source/Launchpad-Commands/LaunchpadTCPCommandServerOption.class.st +++ b/source/Launchpad-Commands-Pharo/LaunchpadTCPCommandServerOption.class.st @@ -4,7 +4,7 @@ Class { #instVars : [ 'command' ], - #category : #'Launchpad-Commands' + #category : #'Launchpad-Commands-Pharo' } { #category : #'instance creation' } @@ -13,6 +13,12 @@ LaunchpadTCPCommandServerOption class >> for: aCommand [ ^ self new initializeFor: aCommand ] +{ #category : #testing } +LaunchpadTCPCommandServerOption class >> isExtendedStartingOption [ + + ^ true +] + { #category : #testing } LaunchpadTCPCommandServerOption >> canHandle: argument [ diff --git a/source/Launchpad-Commands/TCPCommandServer.class.st b/source/Launchpad-Commands-Pharo/TCPCommandServer.class.st similarity index 97% rename from source/Launchpad-Commands/TCPCommandServer.class.st rename to source/Launchpad-Commands-Pharo/TCPCommandServer.class.st index f719058..c7cf0fd 100644 --- a/source/Launchpad-Commands/TCPCommandServer.class.st +++ b/source/Launchpad-Commands-Pharo/TCPCommandServer.class.st @@ -7,7 +7,7 @@ Class { 'connectionSocket', 'serverProcess' ], - #category : #'Launchpad-Commands' + #category : #'Launchpad-Commands-Pharo' } { #category : #'instance creation' } diff --git a/source/Launchpad-Commands-Pharo/package.st b/source/Launchpad-Commands-Pharo/package.st new file mode 100644 index 0000000..d1fc72f --- /dev/null +++ b/source/Launchpad-Commands-Pharo/package.st @@ -0,0 +1 @@ +Package { #name : #'Launchpad-Commands-Pharo' } diff --git a/source/Launchpad-Commands-Tests/LaunchpadRootCommandTest.class.st b/source/Launchpad-Commands-Tests/LaunchpadRootCommandTest.class.st index b7771b3..50f6925 100644 --- a/source/Launchpad-Commands-Tests/LaunchpadRootCommandTest.class.st +++ b/source/Launchpad-Commands-Tests/LaunchpadRootCommandTest.class.st @@ -10,6 +10,14 @@ Class { #category : #'Launchpad-Commands-Tests' } +{ #category : #private } +LaunchpadRootCommandTest >> assert: string isLineEndingInsensitiveEqualsTo: anotherString [ + + self + assert: (string withLineEndings: String lf) + equals: (anotherString withLineEndings: String lf) +] + { #category : #private } LaunchpadRootCommandTest >> assertCommandCanHandleNextArgumentIn: context [ @@ -36,6 +44,13 @@ LaunchpadRootCommandTest >> createFileNamed: fileName containing: fileContents d ] ensure: [ fileReference ensureDelete ] ] +{ #category : #private } +LaunchpadRootCommandTest >> existsExtendedStartingOptions [ + + ^ LaunchpadOption allLeafSubclasses anySatisfy: [ :optionClass | + optionClass isExtendedStartingOption ] +] + { #category : #private } LaunchpadRootCommandTest >> expectedExplainBrokenHelpOutput [ @@ -74,8 +89,12 @@ LaunchpadRootCommandTest >> expectedListHelpOutput [ { #category : #private } LaunchpadRootCommandTest >> expectedStartHelpOutput [ - ^ 'NAME launchpad-start - Start the selected applicationSYNOPSYS launchpad start [--help|-h] [--debug-mode] [--settings-file=%] [--enable-tcp-command-server=%] [--enable-structured-logging] % [%]DESCRIPTION Start the application selected via %.Application configuration is made by the command-line via %, using environment variables or settings files.Execute launchpad explain % to get a list of valid configuration parameters.OPTIONS -h, --help Print this help message and exit. --debug-mode Enable the debugging mode. The image will not quit on unexpected errors. This configuration can be used in the application to improve the debugging experience. --settings-file=% Provide application configuration via a settings file. This option can occur several times to configure more than one settings file. Supported file settings formats are INI and JSON. --enable-tcp-command-server=% Enable a TCP command server. This can be used to send commands controlling the application using a TCP port. --enable-structured-logging Enable structured logging. When enabled the log will be emitted in JSON format.' - expandMacros + ^ (self existsExtendedStartingOptions + ifTrue: [ + 'NAME launchpad-start - Start the selected applicationSYNOPSYS launchpad start [--help|-h] [--debug-mode] [--settings-file=%] [--enable-structured-logging] [--enable-tcp-command-server=%] % [%]DESCRIPTION Start the application selected via %.Application configuration is made by the command-line via %, using environment variables or settings files.Execute launchpad explain % to get a list of valid configuration parameters.OPTIONS -h, --help Print this help message and exit. --debug-mode Enable the debugging mode. The image will not quit on unexpected errors. This configuration can be used in the application to improve the debugging experience. --settings-file=% Provide application configuration via a settings file. This option can occur several times to configure more than one settings file. Supported file settings formats are INI and JSON. --enable-structured-logging Enable structured logging. When enabled the log will be emitted in JSON format. --enable-tcp-command-server=% Enable a TCP command server. This can be used to send commands controlling the application using a TCP port.' ] + ifFalse: [ + 'NAME launchpad-start - Start the selected applicationSYNOPSYS launchpad start [--help|-h] [--debug-mode] [--settings-file=%] [--enable-structured-logging] % [%]DESCRIPTION Start the application selected via %.Application configuration is made by the command-line via %, using environment variables or settings files.Execute launchpad explain % to get a list of valid configuration parameters.OPTIONS -h, --help Print this help message and exit. --debug-mode Enable the debugging mode. The image will not quit on unexpected errors. This configuration can be used in the application to improve the debugging experience. --settings-file=% Provide application configuration via a settings file. This option can occur several times to configure more than one settings file. Supported file settings formats are INI and JSON. --enable-structured-logging Enable structured logging. When enabled the log will be emitted in JSON format.' ]) + expandMacros ] { #category : #private } @@ -130,8 +149,12 @@ LaunchpadRootCommandTest >> handle: arguments onExitDo: aBlock [ { #category : #private } LaunchpadRootCommandTest >> set: environmentVariableName to: value during: aBlock [ - OSPlatform current environment at: environmentVariableName put: value. - aBlock ensure: [ OSPlatform current environment removeKey: environmentVariableName ] + LanguagePlatform current os + environmentAt: environmentVariableName + put: value. + aBlock ensure: [ + LanguagePlatform current os removeEnvironmentKey: + environmentVariableName ] ] { #category : #running } @@ -540,32 +563,6 @@ LaunchpadRootCommandTest >> testHandlingStartGreeterApplicationEnablingStructure '[INFO] Exit application' ) ] -{ #category : #'tests - handling start subcommand' } -LaunchpadRootCommandTest >> testHandlingStartGreeterApplicationEnablingTCPCommandServer [ - - | output | - - output := self handle: { - 'launchpad'. - 'start'. - '--debug-mode'. - '--enable-tcp-command-server=0'. - LaunchpadGreeterApplication commandName. - '--name=John' }. - - self assert: output equals: 'Hi John!' expandMacros. - - self assertLogRecordsMatch: #( - '[INFO] Receiving commands over TCP/0' - '[INFO] greeter [v1.0.0] - A greetings application' - '[INFO] Obtaining configuration...' - '[WARNING] "Title" parameter not provided. Using default.' - '[INFO] Name: John' - '[INFO] Title: ' - '[INFO] Obtaining configuration... [DONE]' - '[INFO] Exit application' ) -] - { #category : #'tests - handling start subcommand' } LaunchpadRootCommandTest >> testHandlingStartGreeterApplicationInDebugMode [ @@ -777,7 +774,7 @@ LaunchpadRootCommandTest >> testHandlingStartSubcommandHelp [ onExitDo: [ :exit | self assert: exit isSuccess ]. self - assert: output equals: self expectedStartHelpOutput; + assert: output isLineEndingInsensitiveEqualsTo: self expectedStartHelpOutput; assertThereAreNoLogRecords ] @@ -791,7 +788,7 @@ LaunchpadRootCommandTest >> testHandlingStartSubcommandShortHelp [ onExitDo: [ :exit | self assert: exit isSuccess ]. self - assert: output equals: self expectedStartHelpOutput; + assert: output isLineEndingInsensitiveEqualsTo: self expectedStartHelpOutput; assertThereAreNoLogRecords ] @@ -799,24 +796,26 @@ LaunchpadRootCommandTest >> testHandlingStartSubcommandShortHelp [ LaunchpadRootCommandTest >> testHandlingStopBrokenApplicationWhileRunning [ | semaphore | - semaphore := Semaphore new. - [ - [ self handle: #( 'launchpad' 'start' '--debug-mode' 'broken' '--raise-error' ) ] - on: Error - do: [ :error | - semaphore wait. - error return - ] - ] forkAt: Processor userInterruptPriority named: 'Launching broken application'. + LanguagePlatform current + fork: [ + [ + self handle: + #( 'launchpad' 'start' '--debug-mode' 'broken' '--raise-error' ) ] + on: Error + do: [ :error | + semaphore wait. + error return ] ] + named: 'Launching broken application' + at: Processor userInterruptPriority. LaunchpadApplication currentlyRunning stop. semaphore signal. self assertLogRecordsMatch: #( '[INFO] broken [v0.0.1] - A broken application' - '[INFO] Obtaining configuration...' + '[INFO] Obtaining configuration...' '[INFO] Obtaining configuration... [DONE]' ) ] @@ -841,7 +840,7 @@ LaunchpadRootCommandTest >> testPrintHelpOn [ help := String streamContents: [ :stream | command printHelpOn: stream ]. - self assert: help equals: 'NAME + self assert: help isLineEndingInsensitiveEqualsTo: 'NAME launchpad - A minimal application launcher SYNOPSYS launchpad [--version] [--help|-h] diff --git a/source/Launchpad-Commands/LaunchpadOption.class.st b/source/Launchpad-Commands/LaunchpadOption.class.st index f31eae2..04940d4 100644 --- a/source/Launchpad-Commands/LaunchpadOption.class.st +++ b/source/Launchpad-Commands/LaunchpadOption.class.st @@ -4,6 +4,15 @@ Class { #category : #'Launchpad-Commands' } +{ #category : #querying } +LaunchpadOption class >> additionalStartingOptionsFor: startApplicationCommand [ + + ^ self allLeafSubclasses + select: [ :optionClass | optionClass isExtendedStartingOption ] + thenCollect: [ :optionClass | + optionClass for: startApplicationCommand ] +] + { #category : #testing } LaunchpadOption class >> isAbstract [ @@ -11,6 +20,12 @@ LaunchpadOption class >> isAbstract [ ^ self = LaunchpadOption ] +{ #category : #testing } +LaunchpadOption class >> isExtendedStartingOption [ + + ^ false +] + { #category : #testing } LaunchpadOption >> canHandle: argument [ diff --git a/source/Launchpad-Commands/LaunchpadRootCommand.class.st b/source/Launchpad-Commands/LaunchpadRootCommand.class.st index 9f330f1..ca8072b 100644 --- a/source/Launchpad-Commands/LaunchpadRootCommand.class.st +++ b/source/Launchpad-Commands/LaunchpadRootCommand.class.st @@ -17,11 +17,11 @@ LaunchpadRootCommand class >> commandName [ ^ 'launchpad' ] -{ #category : #initialization } +{ #category : #'class initialization' } LaunchpadRootCommand class >> initialize [ - self version: ( VersionFromRepositoryResolver new valueFor: #BaselineOfLaunchpad ) + self version: ( VersionFromRepositoryResolver new valueFor: 'Launchpad' ) ] { #category : #accessing } diff --git a/source/Launchpad-Commands/LaunchpadStartApplicationCommand.class.st b/source/Launchpad-Commands/LaunchpadStartApplicationCommand.class.st index 4163bb0..8e77fa1 100644 --- a/source/Launchpad-Commands/LaunchpadStartApplicationCommand.class.st +++ b/source/Launchpad-Commands/LaunchpadStartApplicationCommand.class.st @@ -39,19 +39,6 @@ LaunchpadStartApplicationCommand >> enableStructuredLogging [ ] ensure: [ StandardStreamLogger onStandardError stop ] ] -{ #category : #configuring } -LaunchpadStartApplicationCommand >> enableTCPCommandServerListeningOn: listeningPort [ - - commandServer := TCPCommandServer listeningOn: listeningPort. - commandServer registerCommandNamed: 'SHUTDOWN' executing: [ - [ - LaunchpadApplication currentlyRunning - stop; - exitSuccess - ] forkAt: Processor timingPriority named: 'Launchpad shutdown process' - ] -] - { #category : #evaluating } LaunchpadStartApplicationCommand >> evaluateWithin: context [ @@ -66,12 +53,12 @@ LaunchpadStartApplicationCommand >> evaluateWithin: context [ LaunchpadStartApplicationCommand >> initialize [ super initialize. - options := Array - with: ( LaunchpadHelpOption for: self ) - with: ( LaunchpadDebugModeOption for: self ) - with: ( LaunchpadSettingsFileOption for: self ) - with: ( LaunchpadTCPCommandServerOption for: self ) - with: ( LaunchpadStructuredLoggingOption for: self ). + options := OrderedCollection + with: (LaunchpadHelpOption for: self) + with: (LaunchpadDebugModeOption for: self) + with: (LaunchpadSettingsFileOption for: self) + with: (LaunchpadStructuredLoggingOption for: self). + options addAll: (LaunchpadOption additionalStartingOptionsFor: self). baseConfigurationProvider := NullConfigurationProvider new. applicationMode := ReleasedApplicationMode new. commandServer := NullCommandServer new diff --git a/source/Launchpad-Configuration-GS64-Extensions/CharacterCollection.extension.st b/source/Launchpad-Configuration-GS64-Extensions/CharacterCollection.extension.st new file mode 100644 index 0000000..9eb8248 --- /dev/null +++ b/source/Launchpad-Configuration-GS64-Extensions/CharacterCollection.extension.st @@ -0,0 +1,10 @@ +Extension { #name : #CharacterCollection } + +{ #category : #'*Launchpad-Configuration-GS64-Extensions' } +CharacterCollection >> asBoolean [ + + self asLowercase = 'true' ifTrue: [ ^ true ]. + self asLowercase = 'false' ifTrue: [ ^ false ]. + + InstanceCreationFailed signal: ( '<1s> can''t be interpreted as a boolean' expandMacrosWith: self ) +] diff --git a/source/Launchpad-Configuration-GS64-Extensions/package.st b/source/Launchpad-Configuration-GS64-Extensions/package.st new file mode 100644 index 0000000..0b24836 --- /dev/null +++ b/source/Launchpad-Configuration-GS64-Extensions/package.st @@ -0,0 +1 @@ +Package { #name : #'Launchpad-Configuration-GS64-Extensions' } diff --git a/source/Launchpad-Configuration/String.extension.st b/source/Launchpad-Configuration-Pharo-Extensions/String.extension.st similarity index 80% rename from source/Launchpad-Configuration/String.extension.st rename to source/Launchpad-Configuration-Pharo-Extensions/String.extension.st index e54e124..b4dbaf9 100644 --- a/source/Launchpad-Configuration/String.extension.st +++ b/source/Launchpad-Configuration-Pharo-Extensions/String.extension.st @@ -1,6 +1,6 @@ Extension { #name : #String } -{ #category : #'*Launchpad-Configuration' } +{ #category : #'*Launchpad-Configuration-Pharo-Extensions' } String >> asBoolean [ self asLowercase = 'true' ifTrue: [ ^ true ]. diff --git a/source/Launchpad-Configuration-Pharo-Extensions/package.st b/source/Launchpad-Configuration-Pharo-Extensions/package.st new file mode 100644 index 0000000..70f4914 --- /dev/null +++ b/source/Launchpad-Configuration-Pharo-Extensions/package.st @@ -0,0 +1 @@ +Package { #name : #'Launchpad-Configuration-Pharo-Extensions' } diff --git a/source/Launchpad-Configuration-Tests/ApplicationConfigurationTest.class.st b/source/Launchpad-Configuration-Tests/ApplicationConfigurationTest.class.st index 8d609d2..69edb0a 100644 --- a/source/Launchpad-Configuration-Tests/ApplicationConfigurationTest.class.st +++ b/source/Launchpad-Configuration-Tests/ApplicationConfigurationTest.class.st @@ -7,6 +7,14 @@ Class { #category : #'Launchpad-Configuration-Tests' } +{ #category : #private } +ApplicationConfigurationTest >> assert: string isLineEndingInsensitiveEqualsTo: anotherString [ + + self + assert: (string withLineEndings: String lf) + equals: (anotherString withLineEndings: String lf) +] + { #category : #private } ApplicationConfigurationTest >> commandLineProviderOver: arguments [ @@ -101,7 +109,7 @@ ApplicationConfigurationTest >> testAsEnvironment [ configuration := self newApplicationConfiguration. env := String streamContents: [ :stream | configuration asEnvironmentOn: stream ]. - self assert: env equals: '# Port + self assert: env isLineEndingInsensitiveEqualsTo: '# Port COMMUNICATIONS__HTTP__PORT=8086 # Scheme. Defaults to https COMMUNICATIONS__HTTP__SCHEME=https @@ -119,7 +127,7 @@ ApplicationConfigurationTest >> testAsIniFile [ configuration := self newApplicationConfiguration. ini := String streamContents: [ :stream | configuration asIniFileOn: stream ]. - self assert: ini equals: '; Public URL + self assert: ini isLineEndingInsensitiveEqualsTo: '; Public URL publicURL = https://api.example.com/ [Communications.HTTP] diff --git a/source/Launchpad-Configuration-Tests/ConfigurationFromEnvironmentProviderTest.class.st b/source/Launchpad-Configuration-Tests/ConfigurationFromEnvironmentProviderTest.class.st index 1ffe264..69f5735 100644 --- a/source/Launchpad-Configuration-Tests/ConfigurationFromEnvironmentProviderTest.class.st +++ b/source/Launchpad-Configuration-Tests/ConfigurationFromEnvironmentProviderTest.class.st @@ -10,8 +10,12 @@ Class { { #category : #private } ConfigurationFromEnvironmentProviderTest >> set: environmentVariableName to: value during: aBlock [ - OSPlatform current environment at: environmentVariableName put: value. - aBlock ensure: [ OSPlatform current environment removeKey: environmentVariableName ] + LanguagePlatform current os + environmentAt: environmentVariableName + put: value. + aBlock ensure: [ + LanguagePlatform current os removeEnvironmentKey: + environmentVariableName ] ] { #category : #tests } diff --git a/source/Launchpad-Configuration-Tests/MandatoryConfigurationParameterTest.class.st b/source/Launchpad-Configuration-Tests/MandatoryConfigurationParameterTest.class.st index 46501d5..a45836c 100644 --- a/source/Launchpad-Configuration-Tests/MandatoryConfigurationParameterTest.class.st +++ b/source/Launchpad-Configuration-Tests/MandatoryConfigurationParameterTest.class.st @@ -15,6 +15,16 @@ MandatoryConfigurationParameterTest >> commandLineProviderSetting: aParameter to ( '--<1s>=<2s>' expandMacrosWith: aParameter commandLineArgumentName with: aString ) ) ) ] +{ #category : #private } +MandatoryConfigurationParameterTest >> numberConversionAction [ + + ^ [ :value | + AssertionChecker + enforce: [ value isAllDigits ] + because: 'Expected a number'. + value asNumber ] +] + { #category : #tests } MandatoryConfigurationParameterTest >> testAccessing [ @@ -99,7 +109,7 @@ MandatoryConfigurationParameterTest >> testNumberTransformation [ parameter := MandatoryConfigurationParameter named: 'port' describedBy: 'The number of the port to listen' - convertingWith: #asNumber. + convertingWith: self numberConversionAction. self assert: ( self valueWhenSetting: parameter to: '1' ) equals: 1. self assert: ( self valueWhenSetting: parameter to: '01' ) equals: 1. @@ -108,12 +118,12 @@ MandatoryConfigurationParameterTest >> testNumberTransformation [ self should: [ self valueWhenSetting: parameter to: 'a story about an API' ] raise: Error - withMessageText: 'Reading a number failed: a digit between 0 and 9 expected'. + withMessageText: 'Expected a number'. self should: [ self valueWhenSetting: parameter to: '' ] raise: Error - withMessageText: 'Reading a number failed: a digit between 0 and 9 expected' + withMessageText: 'Expected a number' ] { #category : #tests } diff --git a/source/Launchpad-Configuration-Tests/OptionalConfigurationParameterTest.class.st b/source/Launchpad-Configuration-Tests/OptionalConfigurationParameterTest.class.st index 3a10448..d250865 100644 --- a/source/Launchpad-Configuration-Tests/OptionalConfigurationParameterTest.class.st +++ b/source/Launchpad-Configuration-Tests/OptionalConfigurationParameterTest.class.st @@ -15,6 +15,16 @@ OptionalConfigurationParameterTest >> commandLineProviderSetting: aParameter to: ( '--<1s>=<2s>' expandMacrosWith: aParameter commandLineArgumentName with: aString ) ) ) ] +{ #category : #private } +OptionalConfigurationParameterTest >> numberConversionAction [ + + ^ [ :value | + AssertionChecker + enforce: [ value isAllDigits ] + because: 'Expected a number'. + value asNumber ] +] + { #category : #tests } OptionalConfigurationParameterTest >> testAccessing [ @@ -104,7 +114,7 @@ OptionalConfigurationParameterTest >> testNumberTransformation [ named: 'port' describedBy: 'The service''s listening port' defaultingTo: 8080 - convertingWith: #asNumber. + convertingWith: self numberConversionAction. self assert: ( self valueWhenSetting: parameter to: '1' ) equals: 1. self assert: ( self valueWhenSetting: parameter to: '01' ) equals: 1. @@ -113,12 +123,12 @@ OptionalConfigurationParameterTest >> testNumberTransformation [ self should: [ self valueWhenSetting: parameter to: 'a story about an API' ] raise: Error - withMessageText: 'Reading a number failed: a digit between 0 and 9 expected'. + withMessageText: 'Expected a number'. self should: [ self valueWhenSetting: parameter to: '' ] raise: Error - withMessageText: 'Reading a number failed: a digit between 0 and 9 expected' + withMessageText: 'Expected a number' ] { #category : #tests } diff --git a/source/Launchpad-Configuration/ConfigurationFromEnvironmentProvider.class.st b/source/Launchpad-Configuration/ConfigurationFromEnvironmentProvider.class.st index ee4acba..ed45bd1 100644 --- a/source/Launchpad-Configuration/ConfigurationFromEnvironmentProvider.class.st +++ b/source/Launchpad-Configuration/ConfigurationFromEnvironmentProvider.class.st @@ -34,7 +34,7 @@ ConfigurationFromEnvironmentProvider >> reloadConfiguration [ { #category : #resolving } ConfigurationFromEnvironmentProvider >> valueFor: aConfigurationParameter ifFound: aPresentBlock ifNone: aFailBlock [ - ^ OSPlatform current + ^ LanguagePlatform current os environmentAt: aConfigurationParameter environmentVariableName ifPresent: aPresentBlock ifAbsent: [ diff --git a/source/Launchpad-Configuration/ConfigurationFromJsonSettingsFileProvider.class.st b/source/Launchpad-Configuration/ConfigurationFromJsonSettingsFileProvider.class.st index 29e3bac..45c0302 100644 --- a/source/Launchpad-Configuration/ConfigurationFromJsonSettingsFileProvider.class.st +++ b/source/Launchpad-Configuration/ConfigurationFromJsonSettingsFileProvider.class.st @@ -21,6 +21,13 @@ ConfigurationFromJsonSettingsFileProvider class >> loading: aJsonFileReference c ^ self new initializeLoading: aJsonFileReference chainedWith: aConfigurationProvider ] +{ #category : #private } +ConfigurationFromJsonSettingsFileProvider >> asJsonPath: aConfigurationParameter [ + + ^ (aConfigurationParameter sectionsAsAttributeNames copyWith: + aConfigurationParameter attributeName) collect: #asSymbol +] + { #category : #initialization } ConfigurationFromJsonSettingsFileProvider >> initializeLoading: aFileReference chainedWith: aConfigurationProvider [ @@ -46,9 +53,11 @@ ConfigurationFromJsonSettingsFileProvider >> reloadConfiguration [ { #category : #resolving } ConfigurationFromJsonSettingsFileProvider >> valueFor: aConfigurationParameter ifFound: aPresentBlock ifNone: aFailBlock [ - ^ ( json atPath: - ( aConfigurationParameter sectionsAsAttributeNames copyWith: - aConfigurationParameter attributeName ) ) ifNil: [ - nextProvider valueFor: aConfigurationParameter ifFound: aPresentBlock ifNone: aFailBlock ] - ifNotNil: aPresentBlock + ^ (json atPath: (self asJsonPath: aConfigurationParameter)) + ifNil: [ + nextProvider + valueFor: aConfigurationParameter + ifFound: aPresentBlock + ifNone: aFailBlock ] + ifNotNil: [ :value | aPresentBlock cull: value ] ] diff --git a/source/Launchpad-Configuration/OSPlatform.extension.st b/source/Launchpad-Configuration/OSPlatform.extension.st deleted file mode 100644 index b985d83..0000000 --- a/source/Launchpad-Configuration/OSPlatform.extension.st +++ /dev/null @@ -1,10 +0,0 @@ -Extension { #name : #OSPlatform } - -{ #category : #'*Launchpad-Configuration' } -OSPlatform >> environmentAt: aVariableName ifPresent: aBlock ifAbsent: anAbsentBlock [ - - ^ self environment - at: aVariableName - ifPresent: aBlock - ifAbsent: anAbsentBlock -] diff --git a/source/Launchpad-GS64-Compatibility/CommandLineArguments.extension.st b/source/Launchpad-GS64-Compatibility/CommandLineArguments.extension.st new file mode 100644 index 0000000..2ee49f2 --- /dev/null +++ b/source/Launchpad-GS64-Compatibility/CommandLineArguments.extension.st @@ -0,0 +1,19 @@ +Extension { #name : #CommandLineArguments } + +{ #category : #'*Launchpad-GS64-Compatibility' } +CommandLineArguments >> allFilesWithExtension: anExtension [ + ^ self arguments select: [ :arg| + arg endsWith: anExtension ] +] + +{ #category : #'*Launchpad-GS64-Compatibility' } +CommandLineArguments >> copyWithoutPassword [ + ^ self class withArguments: (arguments reject: [ :each | each beginsWith: '--deploymentPassword' ]) +] + +{ #category : #'*Launchpad-GS64-Compatibility' } +CommandLineArguments >> hasFileWithExtension: aFileExtension [ + "return true if the first argument has the given file extension" + ^ self arguments anySatisfy: [ :arg| + arg endsWith: aFileExtension] +] diff --git a/source/Launchpad-GS64-Compatibility/CommandLineArgumentsTest.extension.st b/source/Launchpad-GS64-Compatibility/CommandLineArgumentsTest.extension.st new file mode 100644 index 0000000..6dfae2e --- /dev/null +++ b/source/Launchpad-GS64-Compatibility/CommandLineArgumentsTest.extension.st @@ -0,0 +1,13 @@ +Extension { #name : #CommandLineArgumentsTest } + +{ #category : #'*Launchpad-GS64-Compatibility' } +CommandLineArgumentsTest >> testAllFileTyped [ + self assert: (self commandLine allFilesWithExtension: #txt) equals: #('noOpt2.txt' 'opt12.txt'). + self assertEmpty: (self commandLine allFilesWithExtension: #foo) +] + +{ #category : #'*Launchpad-GS64-Compatibility' } +CommandLineArgumentsTest >> testHasFilesTyped [ + self assert: (self commandLine hasFileWithExtension: #txt). + self deny: (self commandLine hasFileWithExtension: #foo) +] diff --git a/source/Launchpad-GS64-Compatibility/CommandLineHandler.extension.st b/source/Launchpad-GS64-Compatibility/CommandLineHandler.extension.st new file mode 100644 index 0000000..09897ef --- /dev/null +++ b/source/Launchpad-GS64-Compatibility/CommandLineHandler.extension.st @@ -0,0 +1,88 @@ +Extension { #name : #CommandLineHandler } + +{ #category : #'*Launchpad-GS64-Compatibility' } +CommandLineHandler >> << aString [ + ^ self stdout + nextPutAll: aString; + yourself +] + +{ #category : #'*Launchpad-GS64-Compatibility' } +CommandLineHandler >> activateHelp [ + "Default help implementation, running #help if the only argument is --help " + ((self hasOption: 'help') and: [ self arguments size = 1 ]) + ifTrue: [ + self help. + ^ true ]. + ^ false +] + +{ #category : #'*Launchpad-GS64-Compatibility' } +CommandLineHandler >> activateHelpWithoutArguments [ + "Default help implementation, running #help if the there is no argument or a single one which is --help " + ((self hasOption: 'help') or: [ self arguments isEmpty ]) + ifTrue: [ + self help. + ^ true ]. + ^ false +] + +{ #category : #'*Launchpad-GS64-Compatibility' } +CommandLineHandler >> exitFailure [ + ^ self exitFailure: 'Command line handler failed' +] + +{ #category : #'*Launchpad-GS64-Compatibility' } +CommandLineHandler >> exitFailure: aMessage [ + self hasSessionChanged + ifTrue: [ ^ self ]. + Exit signalFailure: aMessage +] + +{ #category : #'*Launchpad-GS64-Compatibility' } +CommandLineHandler >> exitSuccess [ + self hasSessionChanged + ifTrue: [ ^ self ]. + Exit signalSuccess +] + +{ #category : #'*Launchpad-GS64-Compatibility' } +CommandLineHandler >> hasSessionChanged [ + "check whether the session has changed since the commandline handler as been created" + + ^ session ~~ Smalltalk session +] + +{ #category : #'*Launchpad-GS64-Compatibility' } +CommandLineHandler >> help [ + "This is a crude default help implementation." + self printHelp. + Smalltalk isInteractive + ifFalse: [ self exitSuccess ] +] + +{ #category : #'*Launchpad-GS64-Compatibility' } +CommandLineHandler >> initializeSession [ + + session := Smalltalk session +] + +{ #category : #'*Launchpad-GS64-Compatibility' } +CommandLineHandler >> printHelp [ + self stderr + nextPutAll: self class comment; lf +] + +{ #category : #'*Launchpad-GS64-Compatibility' } +CommandLineHandler >> quit [ + self exitSuccess +] + +{ #category : #'*Launchpad-GS64-Compatibility' } +CommandLineHandler class >> requireDeploymentPassword [ + "Here so that we can distinguish user application services command line handlers from usual pharo command line handlers. + The objective is to be able to filter command line accessible by the user. + Command line handlers that are not application services can be protected by a password in deployment." + + ^ true +] diff --git a/source/Launchpad-GS64-Compatibility/Rowan.class.st b/source/Launchpad-GS64-Compatibility/Rowan.class.st new file mode 100644 index 0000000..107594d --- /dev/null +++ b/source/Launchpad-GS64-Compatibility/Rowan.class.st @@ -0,0 +1,8 @@ +" +Rowan is a GS64 class, this is a placeholder so we can extend it. +" +Class { + #name : #Rowan, + #superclass : #Object, + #category : #'Launchpad-GS64-Compatibility' +} diff --git a/source/Launchpad-GS64-Compatibility/package.st b/source/Launchpad-GS64-Compatibility/package.st new file mode 100644 index 0000000..d34708f --- /dev/null +++ b/source/Launchpad-GS64-Compatibility/package.st @@ -0,0 +1 @@ +Package { #name : #'Launchpad-GS64-Compatibility' } diff --git a/source/Launchpad-GS64-Extensions-Tests/CommandLineArgumentsTest.class.st b/source/Launchpad-GS64-Extensions-Tests/CommandLineArgumentsTest.class.st new file mode 100644 index 0000000..94b4d01 --- /dev/null +++ b/source/Launchpad-GS64-Extensions-Tests/CommandLineArgumentsTest.class.st @@ -0,0 +1,77 @@ +" +That class tests the API given by AbstractUserInput. Since that class is obviously abstract, it can't be instantiated, the tests are running on CommandLine (which inherits from AbstractUserInput) +" +Class { + #name : #CommandLineArgumentsTest, + #superclass : #TestCase, + #instVars : [ + 'commandLine' + ], + #category : #'Launchpad-GS64-Extensions-Tests' +} + +{ #category : #accessing } +CommandLineArgumentsTest >> commandLine [ + ^ commandLine +] + +{ #category : #accessing } +CommandLineArgumentsTest >> parameters [ + ^ #('noOpt1' 'noOpt2.txt' 'noOpt3.avi' '--option1' 'opt11' 'opt12.txt' 'opt13.avi' '--option2' '--option3' 'opt31.st' 'opt32' '--option4' '--option5=option5Value' '-o' 'oValue') +] + +{ #category : #running } +CommandLineArgumentsTest >> setUp [ + super setUp. + commandLine := CommandLineArguments withArguments: self parameters +] + +{ #category : #tests } +CommandLineArgumentsTest >> testAllParameters [ + self assertCollection: self commandLine arguments equals: self parameters +] + +{ #category : #tests } +CommandLineArgumentsTest >> testHasOption [ + self assert: (self commandLine hasOption: #option1). + self assert: (self commandLine hasOption: #option4). + self assert: (self commandLine hasOption: #option5). + self assert: (self commandLine hasOption: #o). + self deny: (self commandLine hasOption: #foo). + self deny: (self commandLine hasOption: #option5Value) +] + +{ #category : #tests } +CommandLineArgumentsTest >> testHasParameters [ + self assert: (self commandLine hasArguments) +] + +{ #category : #tests } +CommandLineArgumentsTest >> testOptionAt [ + self should: [ self commandLine optionAt: #option1 ] raise: Error. + self should: [ self commandLine optionAt: #option4 ] raise: Error. + + self assert: (self commandLine optionAt: #option5) equals: 'option5Value'. + self should: [ self commandLine optionAt: #foo ] raise: Error. + + self assert: (self commandLine optionAt: #o) equals: 'oValue'. + self should: [ self commandLine optionAt: #x ] raise: Error +] + +{ #category : #tests } +CommandLineArgumentsTest >> testOptionAtifAbsent [ + self assert: (self commandLine optionAt: #option1 ifAbsent: [ nil ]) equals: nil. + self assert: (self commandLine optionAt: #option4 ifAbsent: [ nil ]) equals: nil. + self assert: (self commandLine optionAt: #option5) equals: 'option5Value'. + self assert: (self commandLine optionAt: #foo ifAbsent: [ nil ]) equals: nil. + self assert: (self commandLine optionAt: #o) equals: 'oValue'. + self assert: (self commandLine optionAt: #x ifAbsent: [ nil ]) equals: nil +] + +{ #category : #tests } +CommandLineArgumentsTest >> testParameterAt [ + self assert: (self commandLine argumentAt: 1) equals: 'noOpt1'. + self assert: (self commandLine argumentAt: 4) equals: '--option1'. + self deny: (self commandLine argumentAt: 5) equals: 'foo'. + self deny: (self commandLine argumentAt: 1) isNil +] diff --git a/source/Launchpad-GS64-Extensions-Tests/package.st b/source/Launchpad-GS64-Extensions-Tests/package.st new file mode 100644 index 0000000..9fe684d --- /dev/null +++ b/source/Launchpad-GS64-Extensions-Tests/package.st @@ -0,0 +1 @@ +Package { #name : #'Launchpad-GS64-Extensions-Tests' } diff --git a/source/Launchpad-GS64-Extensions/CommandLineArguments.class.st b/source/Launchpad-GS64-Extensions/CommandLineArguments.class.st new file mode 100644 index 0000000..a523304 --- /dev/null +++ b/source/Launchpad-GS64-Extensions/CommandLineArguments.class.st @@ -0,0 +1,140 @@ +" +The CommandLineArguments represents the arguments passed to the image. +" +Class { + #name : #CommandLineArguments, + #superclass : #Object, + #instVars : [ + 'arguments' + ], + #category : #'Launchpad-GS64-Extensions' +} + +{ #category : #'instance creation' } +CommandLineArguments class >> new [ + + ^ self withArguments: System commandLineArguments +] + +{ #category : #'instance creation' } +CommandLineArguments class >> withArguments: aCollection [ + + ^ super new + initializeWithArguments: aCollection; + yourself +] + +{ #category : #accessing } +CommandLineArguments >> argumentAt: index [ + + ^ arguments at: index +] + +{ #category : #accessing } +CommandLineArguments >> arguments [ + + ^ arguments +] + +{ #category : #copying } +CommandLineArguments >> copySubcommand [ + + ^ self class withArguments: arguments allButFirst +] + +{ #category : #testing } +CommandLineArguments >> hasArguments [ + + ^ arguments notEmpty +] + +{ #category : #testing } +CommandLineArguments >> hasOption: aString [ + + | option | + option := ((self isShort: aString) + ifTrue: [ '-' ] + ifFalse: [ '--' ]) , aString. + (self arguments includes: option) ifTrue: [ ^ true ]. + option := option , '='. + ^ self arguments anySatisfy: [ :arg | arg beginsWith: option ] +] + +{ #category : #testing } +CommandLineArguments >> includesSubCommand: aName [ + + self withFirstArgument: [ :arg | arg = aName ifTrue: [ ^ true ] ]. + ^ false +] + +{ #category : #initialization } +CommandLineArguments >> initializeWithArguments: aCollection [ + + arguments := aCollection +] + +{ #category : #private } +CommandLineArguments >> isShort: aString [ + "One letter strings are considered short options/arguments" + + ^ aString size = 1 +] + +{ #category : #private } +CommandLineArguments >> longOptionAt: aString ifAbsent: absentBlock [ + + | optionStart | + optionStart := '--' , aString , '='. + ^ self arguments + detect: [ :arg | arg beginsWith: optionStart ] + ifFound: [ :option | ('=' split: option) second ] + ifNone: absentBlock +] + +{ #category : #accessing } +CommandLineArguments >> optionAt: aString [ + + ^ self + optionAt: aString + ifAbsent: [ Error signal: 'Could not find option ' , aString ] +] + +{ #category : #accessing } +CommandLineArguments >> optionAt: aString ifAbsent: absentBlock [ + + ^ (self isShort: aString) + ifTrue: [ self shortOptionAt: aString ifAbsent: absentBlock ] + ifFalse: [ self longOptionAt: aString ifAbsent: absentBlock ] +] + +{ #category : #accessing } +CommandLineArguments >> optionAt: aString ifPresent: presentBlock [ + | option | + + option := self optionAt: aString ifAbsent: [ ^ self ]. + ^ presentBlock value: option +] + +{ #category : #accessing } +CommandLineArguments >> optionAt: aString ifPresent: presentBlock ifAbsent: absentBlock [ + + | option | + option := self optionAt: aString ifAbsent: [ ^ absentBlock value ]. + ^ presentBlock value: option +] + +{ #category : #private } +CommandLineArguments >> shortOptionAt: aString ifAbsent: absentBlock [ + | index | + index := (arguments indexOf: '-', aString) + 1. + ^ (index <= 1 or: [ index > arguments size ]) + ifFalse: [ arguments at: index ] + ifTrue: absentBlock +] + +{ #category : #accessing } +CommandLineArguments >> withFirstArgument: aBlock [ + + self arguments ifEmpty: [ ^ self ]. + ^ aBlock value: self arguments first +] diff --git a/source/Launchpad-GS64-Extensions/CommandLineHandler.class.st b/source/Launchpad-GS64-Extensions/CommandLineHandler.class.st new file mode 100644 index 0000000..ef082d7 --- /dev/null +++ b/source/Launchpad-GS64-Extensions/CommandLineHandler.class.st @@ -0,0 +1,194 @@ +" +A CommandLineHandler is activated by the CommandLine. + +The responsible handler with the highest priority is selected and its instance-side method #activate is invoked. + +By default the handlers are selected by their class name. In the following shell invocation the FooHandler is chosen: + + pharo Pharo.image FooHandler + +A handler may provide a short name with the class-side #commandName method. If the FooHandler defined #commandName returning 'foo' it would be activated with the following shell invocation: + + pharo Pharo.image foo + +For more sophisticated handler selection the CommandLineHandler should implement the #isResponsibleFor: class-side method. An instance of the current command line options is passed to this method which should then return a boolean. + +Between all the responsible handlers the one with the highest #priority is chosen. To change the priority overwrite the class-side accessor. + +" +Class { + #name : #CommandLineHandler, + #superclass : #Object, + #instVars : [ + 'commandLine', + 'session', + 'stdout', + 'stderr' + ], + #category : #'Launchpad-GS64-Extensions' +} + +{ #category : #'instance creation' } +CommandLineHandler class >> activateWith: aCommandLine [ + + ^ (self commandLine: (self prepareSubcommand: aCommandLine)) activate +] + +{ #category : #accessing } +CommandLineHandler class >> allHandlers [ + ^ self allSubclasses reject: [ :handler| handler isAbstract ] +] + +{ #category : #'instance creation' } +CommandLineHandler class >> commandLine: aCommandLine [ + + ^ self new + commandLine: aCommandLine; + yourself +] + +{ #category : #accessing } +CommandLineHandler class >> commandName [ + + ^ self subclassResponsibility +] + +{ #category : #testing } +CommandLineHandler class >> isAbstract [ + ^ self = CommandLineHandler +] + +{ #category : #testing } +CommandLineHandler class >> isResponsibleFor: aCommandLineArguments [ + ^ aCommandLineArguments includesSubCommand: self commandName +] + +{ #category : #private } +CommandLineHandler class >> prepareSubcommand: commandLineArguments [ + + "strip the subcommand name from the arguments" + commandLineArguments withFirstArgument: [ :arg| + arg = self commandName + ifTrue: [ ^ commandLineArguments copySubcommand ]]. + + "not a subcommand hence we keep the same args" + ^ commandLineArguments +] + +{ #category : #accessing } +CommandLineHandler class >> priority [ + ^ 0 +] + +{ #category : #private } +CommandLineHandler class >> selectHandlersFor: aCommandLine [ + + ^ self allHandlers select: [ :handlerClass| + handlerClass isResponsibleFor: aCommandLine ] +] + +{ #category : #activation } +CommandLineHandler >> activate [ + self subclassResponsibility +] + +{ #category : #'accessing - arguments' } +CommandLineHandler >> argumentAt: anInteger [ + ^ self commandLine argumentAt: anInteger +] + +{ #category : #'accessing - arguments' } +CommandLineHandler >> arguments [ + ^ self commandLine arguments +] + +{ #category : #accessing } +CommandLineHandler >> commandLine [ + ^ commandLine +] + +{ #category : #accessing } +CommandLineHandler >> commandLine: aCommandLine [ + commandLine := aCommandLine +] + +{ #category : #accessing } +CommandLineHandler >> commandName [ + ^ self class commandName +] + +{ #category : #testing } +CommandLineHandler >> hasArguments [ + ^ self commandLine hasArguments +] + +{ #category : #'accessing - arguments' } +CommandLineHandler >> hasOption: aString [ + ^ self commandLine hasOption: aString +] + +{ #category : #initialization } +CommandLineHandler >> initialize [ + + super initialize. + self + initializeSession; + initializeStdout; + initializeStderr +] + +{ #category : #initialization } +CommandLineHandler >> initializeSession [ + + session := GsCurrentSession currentSession +] + +{ #category : #initialization } +CommandLineHandler >> initializeStderr [ + + stderr := Stdio stderr +] + +{ #category : #initialization } +CommandLineHandler >> initializeStdout [ + + stdout := Stdio stdout +] + +{ #category : #accessing } +CommandLineHandler >> name [ + ^ self printString +] + +{ #category : #'accessing - arguments' } +CommandLineHandler >> optionAt: aString [ + ^ self commandLine optionAt: aString +] + +{ #category : #'accessing - arguments' } +CommandLineHandler >> optionAt: aString ifAbsent: absentBlock [ + ^ self commandLine + optionAt: aString ifAbsent: absentBlock +] + +{ #category : #'accessing - arguments' } +CommandLineHandler >> optionAt: aString ifPresent: absentBlock [ + ^ self commandLine + optionAt: aString ifPresent: absentBlock +] + +{ #category : #'accessing - arguments' } +CommandLineHandler >> optionAt: aString ifPresent: presentBlock ifAbsent: absentBlock [ + ^ self commandLine + optionAt: aString ifPresent: presentBlock ifAbsent: absentBlock +] + +{ #category : #accessing } +CommandLineHandler >> stderr [ + ^ stderr +] + +{ #category : #accessing } +CommandLineHandler >> stdout [ + ^ stdout +] diff --git a/source/Launchpad-GS64-Extensions/VersionFromRepositoryResolver.class.st b/source/Launchpad-GS64-Extensions/VersionFromRepositoryResolver.class.st new file mode 100644 index 0000000..97d1fec --- /dev/null +++ b/source/Launchpad-GS64-Extensions/VersionFromRepositoryResolver.class.st @@ -0,0 +1,29 @@ +Class { + #name : #VersionFromRepositoryResolver, + #superclass : #Object, + #category : #'Launchpad-GS64-Extensions' +} + +{ #category : #accessing } +VersionFromRepositoryResolver >> valueFor: projectName [ + + | repoPath commitHash tags | + repoPath := (Rowan projectNamed: projectName) repositoryRoot pathString. + commitHash := (Rowan gitTools + performGitCommand: 'rev-parse' + in: repoPath + with: #( 'HEAD' )) trimSeparators. + tags := (Rowan gitTools + performGitCommand: 'tag' + in: repoPath + with: #( '--points-at HEAD' )) substrings. + ^ String streamContents: [ :stream | + tags do: [ :tag | + stream + nextPutAll: tag; + space ]. + stream + nextPut: $[; + nextPutAll: commitHash; + nextPut: $] ] +] diff --git a/source/Launchpad-GS64-Extensions/package.st b/source/Launchpad-GS64-Extensions/package.st new file mode 100644 index 0000000..8736a8b --- /dev/null +++ b/source/Launchpad-GS64-Extensions/package.st @@ -0,0 +1 @@ +Package { #name : #'Launchpad-GS64-Extensions' } diff --git a/source/Launchpad-SUnit/LaunchpadTest.class.st b/source/Launchpad-SUnit/LaunchpadTest.class.st index 095d3ed..2db6163 100644 --- a/source/Launchpad-SUnit/LaunchpadTest.class.st +++ b/source/Launchpad-SUnit/LaunchpadTest.class.st @@ -78,7 +78,7 @@ LaunchpadTest >> start: aLaunchpadApplication withAll: arguments [ LaunchpadTest >> tearDown [ loggingAsserter stopLoggers. - runningApplication ifNotNil: #stop. + runningApplication ifNotNil: [:application | application stop]. LaunchpadApplication resetCurrentlyRunning. super tearDown ] diff --git a/source/Launchpad-Tracing-GS64/StackTraceTextDumper.class.st b/source/Launchpad-Tracing-GS64/StackTraceTextDumper.class.st new file mode 100644 index 0000000..7ed691a --- /dev/null +++ b/source/Launchpad-Tracing-GS64/StackTraceTextDumper.class.st @@ -0,0 +1,33 @@ +Class { + #name : #StackTraceTextDumper, + #superclass : #StackTraceDumper, + #instVars : [ + 'openStreamAction' + ], + #category : #'Launchpad-Tracing-GS64' +} + +{ #category : #'instance creation' } +StackTraceTextDumper class >> on: anOpenStreamAction [ + + ^ self new initializeOn: anOpenStreamAction +] + +{ #category : #'error handling' } +StackTraceTextDumper >> dumpStackTraceFor: anError [ + + self writeStreamDo: [ :stream | + stream nextPutAll: (GsProcess stackReportToLevel: 300) ] +] + +{ #category : #initialization } +StackTraceTextDumper >> initializeOn: anOpenStreamAction [ + + openStreamAction := anOpenStreamAction +] + +{ #category : #private } +StackTraceTextDumper >> writeStreamDo: dumpAction [ + + openStreamAction value: dumpAction +] diff --git a/source/Launchpad-Tracing-GS64/package.st b/source/Launchpad-Tracing-GS64/package.st new file mode 100644 index 0000000..d60142e --- /dev/null +++ b/source/Launchpad-Tracing-GS64/package.st @@ -0,0 +1 @@ +Package { #name : #'Launchpad-Tracing-GS64' } diff --git a/source/Launchpad-Tracing-Tests/StackTraceBinarySerializerTest.class.st b/source/Launchpad-Tracing-Pharo-Tests/StackTraceBinarySerializerTest.class.st similarity index 96% rename from source/Launchpad-Tracing-Tests/StackTraceBinarySerializerTest.class.st rename to source/Launchpad-Tracing-Pharo-Tests/StackTraceBinarySerializerTest.class.st index 62468a7..ab4b6e1 100644 --- a/source/Launchpad-Tracing-Tests/StackTraceBinarySerializerTest.class.st +++ b/source/Launchpad-Tracing-Pharo-Tests/StackTraceBinarySerializerTest.class.st @@ -4,7 +4,7 @@ A StackTraceBinarySerializerTest is a test class for testing the behavior of Sta Class { #name : #StackTraceBinarySerializerTest, #superclass : #StackTraceDumperTest, - #category : #'Launchpad-Tracing-Tests' + #category : #'Launchpad-Tracing-Pharo-Tests' } { #category : #coverage } diff --git a/source/Launchpad-Tracing-Pharo-Tests/package.st b/source/Launchpad-Tracing-Pharo-Tests/package.st new file mode 100644 index 0000000..5bb2128 --- /dev/null +++ b/source/Launchpad-Tracing-Pharo-Tests/package.st @@ -0,0 +1 @@ +Package { #name : #'Launchpad-Tracing-Pharo-Tests' } diff --git a/source/Launchpad-Tracing/StackTraceTextDumper.class.st b/source/Launchpad-Tracing-Pharo/StackTraceTextDumper.class.st similarity index 94% rename from source/Launchpad-Tracing/StackTraceTextDumper.class.st rename to source/Launchpad-Tracing-Pharo/StackTraceTextDumper.class.st index 9bba028..e7d6624 100644 --- a/source/Launchpad-Tracing/StackTraceTextDumper.class.st +++ b/source/Launchpad-Tracing-Pharo/StackTraceTextDumper.class.st @@ -4,7 +4,7 @@ Class { #instVars : [ 'openStreamAction' ], - #category : #'Launchpad-Tracing' + #category : #'Launchpad-Tracing-Pharo' } { #category : #'instance creation' } diff --git a/source/Launchpad-Tracing-Pharo/package.st b/source/Launchpad-Tracing-Pharo/package.st new file mode 100644 index 0000000..8126339 --- /dev/null +++ b/source/Launchpad-Tracing-Pharo/package.st @@ -0,0 +1 @@ +Package { #name : #'Launchpad-Tracing-Pharo' }