Using GtSubprocess instead of OSSubprocess. Will backporting this to "plain" Pharo be a future issue?

This commit is contained in:
Offray Vladimir Luna Cárdenas 2024-05-19 11:47:59 -05:00
parent c01d31a2a9
commit 5de6a81029
14 changed files with 626 additions and 630 deletions

View File

@ -1,20 +1,20 @@
Class {
#name : #Brew,
#superclass : #Object,
#category : #ExoRepo
}
{ #category : #accessing }
Brew class >> install [
"This is a preliminary starting installation script that is not working.
Dependencies and sudo access are not managed here.
For example, doing 'sudo -S base-devel' on Arch based systems or
'brew intall gcc' is yet not managed here."
Smalltalk os isWindows ifTrue: [ ^ nil ].
OSSUnixSubprocess new
shellCommand: '/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"';
redirectStdout;
runAndWaitOnExitDo: [ :command :outString |
^ outString
].
]
Class {
#name : #Brew,
#superclass : #Object,
#category : #ExoRepo
}
{ #category : #accessing }
Brew class >> install [
"This is a preliminary starting installation script that is not working.
Dependencies and sudo access are not managed here.
For example, doing 'sudo -S base-devel' on Arch based systems or
'brew intall gcc' is yet not managed here."
Smalltalk os isWindows ifTrue: [ ^ nil ].
OSSUnixSubprocess new
shellCommand: '/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"';
redirectStdout;
runAndWaitOnExitDo: [ :command :outString |
^ outString
].
]

View File

@ -1,42 +1,42 @@
Class {
#name : #ExoPackage,
#superclass : #Object,
#instVars : [
'repository'
],
#category : #'ExoRepo-External'
}
{ #category : #accessing }
ExoPackage class >> githubAPIEndPoint [
^ 'https://api.github.com/' asUrl
]
{ #category : #accessing }
ExoPackage >> assets [
^ (self lastReleaseData at: 'assets') collect: [:each |
GitHubAsset fromDictionary: each
]
]
{ #category : #accessing }
ExoPackage >> lastReleaseData [
| releasesLink |
releasesLink := self class githubAPIEndPoint addPathSegment: 'repos'.
self repository asUrl segments do: [:segment |
releasesLink addPathSegment: segment
].
releasesLink addPathSegment: 'releases'.
^ (STONJSON
fromString: releasesLink retrieveContents) first
]
{ #category : #accessing }
ExoPackage >> repository [
^ repository
]
{ #category : #accessing }
ExoPackage >> repository: anUrl [
repository := anUrl
]
Class {
#name : #ExoPackage,
#superclass : #Object,
#instVars : [
'repository'
],
#category : #'ExoRepo-External'
}
{ #category : #accessing }
ExoPackage class >> githubAPIEndPoint [
^ 'https://api.github.com/' asUrl
]
{ #category : #accessing }
ExoPackage >> assets [
^ (self lastReleaseData at: 'assets') collect: [:each |
GitHubAsset fromDictionary: each
]
]
{ #category : #accessing }
ExoPackage >> lastReleaseData [
| releasesLink |
releasesLink := self class githubAPIEndPoint addPathSegment: 'repos'.
self repository asUrl segments do: [:segment |
releasesLink addPathSegment: segment
].
releasesLink addPathSegment: 'releases'.
^ (STONJSON
fromString: releasesLink retrieveContents) first
]
{ #category : #accessing }
ExoPackage >> repository [
^ repository
]
{ #category : #accessing }
ExoPackage >> repository: anUrl [
repository := anUrl
]

View File

@ -1,123 +1,123 @@
Class {
#name : #ExoRepo,
#superclass : #Object,
#instVars : [
'repository'
],
#category : #ExoRepo
}
{ #category : #accessing }
ExoRepo class >> index [
"I list a set of external recommended repositories from the Grafoscopio community, in installation order."
| reposIndex repositoryAddresses users |
users := #('Offray' 'mutabiT' 'ruidajo' ).
reposIndex := OrderedCollection new.
repositoryAddresses := users do: [:user |
reposIndex add:
((STONJSON fromString:('https://code.sustrato.red/api/v1/users/', user, '/repos') asZnUrl retrieveContents) collect: [:repo |
(repo at: 'name') -> (repo at: 'html_url')])].
^ (reposIndex flatCollect: [:i | i]) asDictionary
]
{ #category : #accessing }
ExoRepo class >> install: shortNameString [
| repo exoRepo |
[repo := self index at: shortNameString]
onErrorDo: [:err | UIManager default inform: 'No repository named: ', shortNameString, '.
Please run ExoRepo index
to list know repositories or load your own repository with:
ExoRepo new
repository: {repositoryAddres};
load.
replacing {repositoryAddres} with a Git public repository'.^ {self new. err}].
exoRepo := self new repository: repo.
(IceRepositoryCreator new
location: exoRepo local;
remote: (IceGitRemote url: repo);
createRepository) register.
^ exoRepo load
]
{ #category : #accessing }
ExoRepo class >> userDataFolder [
Smalltalk os isWindows
ifTrue: [ ^ FileLocator home / 'AppData' / 'Local' ]
ifFalse: [ ^ FileLocator userData ].
]
{ #category : #accessing }
ExoRepo >> load [
"I load the configuration of this package using a external Gitea repository."
"While more Git independient providers are implemented in Monticello, I will
use Iceberg to download the repository and load it from a local directory"
| localRepo repoName count |
repoName := self repositoryName.
self local exists
ifFalse: [ (IceRepositoryCreator new
location: self local;
remote: (IceGitRemote url: self repository greaseString ", '.git'");
createRepository) register ].
localRepo := 'gitlocal://' , self local fullName.
count := 1.
[ true ]
whileTrue: [ [ ^ Metacello new
repository: localRepo;
baseline: repoName;
onConflictUseLoaded;
onWarningLog;
load ]
on: IceGenericError
do: [ :ex |
Notification
signal: (String with: Character cr) , ex description , (String with: Character cr)
, 'RETRYING ' , count greaseString.
(Delay forSeconds: 2) wait.
ex retry ].
count := count + 1 ]
]
{ #category : #accessing }
ExoRepo >> local [
^ FileLocator localDirectory / 'iceberg' / (self provider) / self repositoryName
]
{ #category : #accessing }
ExoRepo >> printOn: aStream [
super initialize.
self repository ifNotNil: [
aStream nextPutAll: self repositoryName, ' | ', self repository asString]
ifNil: [ aStream nextPutAll: 'ExoRepo without repository' ].
]
{ #category : #accessing }
ExoRepo >> provider [
self repository ifNil: [ ^ nil ].
^ self repository segments first
]
{ #category : #accessing }
ExoRepo >> repository [
^ repository.
]
{ #category : #accessing }
ExoRepo >> repository: aString [
repository := aString asZnUrl
]
{ #category : #accessing }
ExoRepo >> repositoryName [
self repository ifNil: [ ^ self ].
^ self repository segments second
]
{ #category : #accessing }
ExoRepo >> wiki [
^ (self local / 'wiki') ensureCreateDirectory.
]
Class {
#name : #ExoRepo,
#superclass : #Object,
#instVars : [
'repository'
],
#category : #ExoRepo
}
{ #category : #accessing }
ExoRepo class >> index [
"I list a set of external recommended repositories from the Grafoscopio community, in installation order."
| reposIndex repositoryAddresses users |
users := #('Offray' 'mutabiT' 'ruidajo' ).
reposIndex := OrderedCollection new.
repositoryAddresses := users do: [:user |
reposIndex add:
((STONJSON fromString:('https://code.sustrato.red/api/v1/users/', user, '/repos') asZnUrl retrieveContents) collect: [:repo |
(repo at: 'name') -> (repo at: 'html_url')])].
^ (reposIndex flatCollect: [:i | i]) asDictionary
]
{ #category : #accessing }
ExoRepo class >> install: shortNameString [
| repo exoRepo |
[repo := self index at: shortNameString]
onErrorDo: [:err | UIManager default inform: 'No repository named: ', shortNameString, '.
Please run ExoRepo index
to list know repositories or load your own repository with:
ExoRepo new
repository: {repositoryAddres};
load.
replacing {repositoryAddres} with a Git public repository'.^ {self new. err}].
exoRepo := self new repository: repo.
(IceRepositoryCreator new
location: exoRepo local;
remote: (IceGitRemote url: repo);
createRepository) register.
^ exoRepo load
]
{ #category : #accessing }
ExoRepo class >> userDataFolder [
Smalltalk os isWindows
ifTrue: [ ^ FileLocator home / 'AppData' / 'Local' ]
ifFalse: [ ^ FileLocator userData ].
]
{ #category : #accessing }
ExoRepo >> load [
"I load the configuration of this package using a external Gitea repository."
"While more Git independient providers are implemented in Monticello, I will
use Iceberg to download the repository and load it from a local directory"
| localRepo repoName count |
repoName := self repositoryName.
self local exists
ifFalse: [ (IceRepositoryCreator new
location: self local;
remote: (IceGitRemote url: self repository greaseString ", '.git'");
createRepository) register ].
localRepo := 'gitlocal://' , self local fullName.
count := 1.
[ true ]
whileTrue: [ [ ^ Metacello new
repository: localRepo;
baseline: repoName;
onConflictUseLoaded;
onWarningLog;
load ]
on: IceGenericError
do: [ :ex |
Notification
signal: (String with: Character cr) , ex description , (String with: Character cr)
, 'RETRYING ' , count greaseString.
(Delay forSeconds: 2) wait.
ex retry ].
count := count + 1 ]
]
{ #category : #accessing }
ExoRepo >> local [
^ FileLocator localDirectory / 'iceberg' / (self provider) / self repositoryName
]
{ #category : #accessing }
ExoRepo >> printOn: aStream [
super initialize.
self repository ifNotNil: [
aStream nextPutAll: self repositoryName, ' | ', self repository asString]
ifNil: [ aStream nextPutAll: 'ExoRepo without repository' ].
]
{ #category : #accessing }
ExoRepo >> provider [
self repository ifNil: [ ^ nil ].
^ self repository segments first
]
{ #category : #accessing }
ExoRepo >> repository [
^ repository.
]
{ #category : #accessing }
ExoRepo >> repository: aString [
repository := aString asZnUrl
]
{ #category : #accessing }
ExoRepo >> repositoryName [
self repository ifNil: [ ^ self ].
^ self repository segments second
]
{ #category : #accessing }
ExoRepo >> wiki [
^ (self local / 'wiki') ensureCreateDirectory.
]

View File

@ -1,56 +1,56 @@
Class {
#name : #GitHubAsset,
#superclass : #Object,
#instVars : [
'name',
'created',
'updated',
'size',
'downloadLink'
],
#category : #'ExoRepo-External'
}
{ #category : #accessing }
GitHubAsset class >> fromDictionary: aGitHubAssetDictionary [
| response |
response := self new
name: (aGitHubAssetDictionary at: 'name');
size: (aGitHubAssetDictionary at: 'size');
created: (aGitHubAssetDictionary at: 'created_at');
updated: (aGitHubAssetDictionary at: 'updated_at');
downloadLink: (aGitHubAssetDictionary at: 'browser_download_url').
^ response
]
{ #category : #accessing }
GitHubAsset >> created: anObject [
created := anObject
]
{ #category : #accessing }
GitHubAsset >> downloadLink: anUrl [
downloadLink := anUrl
]
{ #category : #accessing }
GitHubAsset >> name: anObject [
name := anObject
]
{ #category : #accessing }
GitHubAsset >> printOn: aStream [
super initialize.
aStream
nextPutAll: '(', name, ')'
]
{ #category : #accessing }
GitHubAsset >> size: anObject [
size := anObject
]
{ #category : #accessing }
GitHubAsset >> updated: anObject [
updated := anObject
]
Class {
#name : #GitHubAsset,
#superclass : #Object,
#instVars : [
'name',
'created',
'updated',
'size',
'downloadLink'
],
#category : #'ExoRepo-External'
}
{ #category : #accessing }
GitHubAsset class >> fromDictionary: aGitHubAssetDictionary [
| response |
response := self new
name: (aGitHubAssetDictionary at: 'name');
size: (aGitHubAssetDictionary at: 'size');
created: (aGitHubAssetDictionary at: 'created_at');
updated: (aGitHubAssetDictionary at: 'updated_at');
downloadLink: (aGitHubAssetDictionary at: 'browser_download_url').
^ response
]
{ #category : #accessing }
GitHubAsset >> created: anObject [
created := anObject
]
{ #category : #accessing }
GitHubAsset >> downloadLink: anUrl [
downloadLink := anUrl
]
{ #category : #accessing }
GitHubAsset >> name: anObject [
name := anObject
]
{ #category : #accessing }
GitHubAsset >> printOn: aStream [
super initialize.
aStream
nextPutAll: '(', name, ')'
]
{ #category : #accessing }
GitHubAsset >> size: anObject [
size := anObject
]
{ #category : #accessing }
GitHubAsset >> updated: anObject [
updated := anObject
]

View File

@ -1,14 +1,14 @@
Extension { #name : #Gofer }
{ #category : #'*ExoRepo' }
Gofer >> repositories [
"Answer the configured monticello repositories."
| result |
result := OrderedCollection withAll: repositories.
packageCacheRepository ifNotNil: [ result addFirst: packageCacheRepository ].
^ result asArray collect: [:each |
(each class = MCHttpRepository and: [each location beginsWith: 'gitea'])
ifTrue: [ MCRepository fromUrl: each location ] ifFalse: [ each ]
].
]
Extension { #name : #Gofer }
{ #category : #'*ExoRepo' }
Gofer >> repositories [
"Answer the configured monticello repositories."
| result |
result := OrderedCollection withAll: repositories.
packageCacheRepository ifNotNil: [ result addFirst: packageCacheRepository ].
^ result asArray collect: [:each |
(each class = MCHttpRepository and: [each location beginsWith: 'gitea'])
ifTrue: [ MCRepository fromUrl: each location ] ifFalse: [ each ]
].
]

View File

@ -1,79 +1,79 @@
Class {
#name : #MCGiteabRepository,
#superclass : #MCGitBasedNetworkRepository,
#category : #ExoRepo
}
{ #category : #accessing }
MCGiteabRepository class >> basicDescription [
^ 'gitea'
]
{ #category : #'as yet unclassified' }
MCGiteabRepository class >> basicFromUrl: aZnUrl [
^ self location: aZnUrl asString
]
{ #category : #'*ExoRepo' }
MCGiteabRepository class >> createRepositoryFromSpec: aMetacelloRepositorySpec on: anIceMetacelloPharoPlatform [
^ anIceMetacelloPharoPlatform createGiteaRepository: aMetacelloRepositorySpec
]
{ #category : #accessing }
MCGiteabRepository class >> isAvailableFor: type [
^ type = 'gitea'
]
{ #category : #private }
MCGiteabRepository class >> projectZipUrlFor: projectPath versionString: versionString [
^ 'https://' , projectPath , '/archive/' , versionString , '.zip'
]
{ #category : #accessing }
MCGiteabRepository class >> urlSchemes [
^ #(gitea)
]
{ #category : #accessing }
MCGiteabRepository >> branches [
"IMPORTANT! This requires the installation of the external multiplatform binary dependency
restify at: https://github.com/itzg/restify/.
This should be installed independiently."
| response |
response := OrderedDictionary new.
OSSUnixSubprocess new
shellCommand: 'restify --class="gt-ellipsis" ', 'https://', self projectPath, '/', self repoPath, '/branches';
redirectStdout;
runAndWaitOnExitDo: [ :command :outString |
outString ifEmpty: [^ command ].
(STON fromString: outString) do: [:each |
response at: (each at: 'text') put: (each at: 'href')
]
].
^ response
]
{ #category : #accessing }
MCGiteabRepository >> calculateRepositoryDirectory [
| directory |
directory := self class
projectDirectoryFrom: self projectPath, '/', self repoPath
version: self projectVersion.
self repoPath isEmpty ifFalse: [
directory := directory parent ].
^ directory
]
{ #category : #accessing }
MCGiteabRepository >> location: aString [
]
{ #category : #accessing }
MCGiteabRepository >> projectVersion [
(projectVersion == nil or: [ projectVersion isEmpty ])
ifTrue: [ projectVersion := self branches keys first ].
^ projectVersion
]
Class {
#name : #MCGiteabRepository,
#superclass : #MCGitBasedNetworkRepository,
#category : #ExoRepo
}
{ #category : #accessing }
MCGiteabRepository class >> basicDescription [
^ 'gitea'
]
{ #category : #'as yet unclassified' }
MCGiteabRepository class >> basicFromUrl: aZnUrl [
^ self location: aZnUrl asString
]
{ #category : #'*ExoRepo' }
MCGiteabRepository class >> createRepositoryFromSpec: aMetacelloRepositorySpec on: anIceMetacelloPharoPlatform [
^ anIceMetacelloPharoPlatform createGiteaRepository: aMetacelloRepositorySpec
]
{ #category : #accessing }
MCGiteabRepository class >> isAvailableFor: type [
^ type = 'gitea'
]
{ #category : #private }
MCGiteabRepository class >> projectZipUrlFor: projectPath versionString: versionString [
^ 'https://' , projectPath , '/archive/' , versionString , '.zip'
]
{ #category : #accessing }
MCGiteabRepository class >> urlSchemes [
^ #(gitea)
]
{ #category : #accessing }
MCGiteabRepository >> branches [
"IMPORTANT! This requires the installation of the external multiplatform binary dependency
restify at: https://github.com/itzg/restify/.
This should be installed independiently."
| response |
response := OrderedDictionary new.
OSSUnixSubprocess new
shellCommand: 'restify --class="gt-ellipsis" ', 'https://', self projectPath, '/', self repoPath, '/branches';
redirectStdout;
runAndWaitOnExitDo: [ :command :outString |
outString ifEmpty: [^ command ].
(STON fromString: outString) do: [:each |
response at: (each at: 'text') put: (each at: 'href')
]
].
^ response
]
{ #category : #accessing }
MCGiteabRepository >> calculateRepositoryDirectory [
| directory |
directory := self class
projectDirectoryFrom: self projectPath, '/', self repoPath
version: self projectVersion.
self repoPath isEmpty ifFalse: [
directory := directory parent ].
^ directory
]
{ #category : #accessing }
MCGiteabRepository >> location: aString [
]
{ #category : #accessing }
MCGiteabRepository >> projectVersion [
(projectVersion == nil or: [ projectVersion isEmpty ])
ifTrue: [ projectVersion := self branches keys first ].
^ projectVersion
]

View File

@ -1,6 +1,6 @@
Extension { #name : #MCGiteabRepository }
{ #category : #'*ExoRepo' }
MCGiteabRepository class >> createRepositoryFromSpec: aMetacelloRepositorySpec on: anIceMetacelloPharoPlatform [
^ anIceMetacelloPharoPlatform createGiteaRepository: aMetacelloRepositorySpec
]
Extension { #name : #MCGiteabRepository }
{ #category : #'*ExoRepo' }
MCGiteabRepository class >> createRepositoryFromSpec: aMetacelloRepositorySpec on: anIceMetacelloPharoPlatform [
^ anIceMetacelloPharoPlatform createGiteaRepository: aMetacelloRepositorySpec
]

View File

@ -1,15 +1,15 @@
Extension { #name : #MetacelloCommonMCSpecLoader }
{ #category : #'*ExoRepo' }
MetacelloCommonMCSpecLoader >> linearLoadPackageSpecs: packageSpecs repositories: repositories [
| gofer sanitizedRepos |
gofer := MetacelloGofer new.
sanitizedRepos := OrderedCollection withAll: repositories.
sanitizedRepos := sanitizedRepos collect: [:each |
(each class = MCHttpRepository and: [each location beginsWith: 'gitea'])
ifTrue: [ MCRepository fromUrl: each location ] ifFalse: [ each ]
].
sanitizedRepos do: [:repo | gofer repository: repo ].
packageSpecs do: [:pkg | pkg loadUsing: self gofer: gofer ].
]
Extension { #name : #MetacelloCommonMCSpecLoader }
{ #category : #'*ExoRepo' }
MetacelloCommonMCSpecLoader >> linearLoadPackageSpecs: packageSpecs repositories: repositories [
| gofer sanitizedRepos |
gofer := MetacelloGofer new.
sanitizedRepos := OrderedCollection withAll: repositories.
sanitizedRepos := sanitizedRepos collect: [:each |
(each class = MCHttpRepository and: [each location beginsWith: 'gitea'])
ifTrue: [ MCRepository fromUrl: each location ] ifFalse: [ each ]
].
sanitizedRepos do: [:repo | gofer repository: repo ].
packageSpecs do: [:pkg | pkg loadUsing: self gofer: gofer ].
]

View File

@ -1,32 +1,32 @@
Extension { #name : #MetacelloPlatform }
{ #category : #'*ExoRepo' }
MetacelloPlatform >> createGiteaRepository: aRepositorySpec [
| cl |
cl := MCGiteabRepository.
^ cl location: aRepositorySpec description
]
{ #category : #'*ExoRepo' }
MetacelloPlatform >> extractTypeFromDescription: description [
description == nil
ifTrue: [ ^ nil ].
((description beginsWith: '/') or: [ description second = $: ])
ifTrue: [ ^ 'directory' ].
(description beginsWith: 'dictionary://')
ifTrue: [ ^ 'dictionary' ].
(description beginsWith: 'filetree://')
ifTrue: [ ^ 'filetree' ].
(description beginsWith: 'tonel://')
ifTrue: [ ^ 'tonel' ].
(description beginsWith: 'github://')
ifTrue: [ ^ 'github' ].
(description beginsWith: 'gitorious://')
ifTrue: [ ^ 'gitorious' ].
(description beginsWith: 'gitea://')
ifTrue: [ ^ 'gitea' ].
(description beginsWith: 'bitbucket://')
ifTrue: [ ^ 'bitbucket' ].
^ 'http'
]
Extension { #name : #MetacelloPlatform }
{ #category : #'*ExoRepo' }
MetacelloPlatform >> createGiteaRepository: aRepositorySpec [
| cl |
cl := MCGiteabRepository.
^ cl location: aRepositorySpec description
]
{ #category : #'*ExoRepo' }
MetacelloPlatform >> extractTypeFromDescription: description [
description == nil
ifTrue: [ ^ nil ].
((description beginsWith: '/') or: [ description second = $: ])
ifTrue: [ ^ 'directory' ].
(description beginsWith: 'dictionary://')
ifTrue: [ ^ 'dictionary' ].
(description beginsWith: 'filetree://')
ifTrue: [ ^ 'filetree' ].
(description beginsWith: 'tonel://')
ifTrue: [ ^ 'tonel' ].
(description beginsWith: 'github://')
ifTrue: [ ^ 'github' ].
(description beginsWith: 'gitorious://')
ifTrue: [ ^ 'gitorious' ].
(description beginsWith: 'gitea://')
ifTrue: [ ^ 'gitea' ].
(description beginsWith: 'bitbucket://')
ifTrue: [ ^ 'bitbucket' ].
^ 'http'
]

View File

@ -1,27 +1,27 @@
"
I'm a a front-end to use multiple package managers like [Scoop](https://scoop.sh/) on Windows and [Brew](https://brew.sh/) on MacOS and Gnu/Linux
"
Class {
#name : #MultiPack,
#superclass : #Object,
#instVars : [
'requirements',
'recommendations',
'repository'
],
#category : #ExoRepo
}
{ #category : #accessing }
MultiPack >> recommendations: aDictionary [
recommendations := aDictionary
]
{ #category : #accessing }
MultiPack >> repository: anObject [
]
{ #category : #accessing }
MultiPack >> requirements: aDictionary [
requirements := aDictionary
]
"
I'm a a front-end to use multiple package managers like [Scoop](https://scoop.sh/) on Windows and [Brew](https://brew.sh/) on MacOS and Gnu/Linux
"
Class {
#name : #MultiPack,
#superclass : #Object,
#instVars : [
'requirements',
'recommendations',
'repository'
],
#category : #ExoRepo
}
{ #category : #accessing }
MultiPack >> recommendations: aDictionary [
recommendations := aDictionary
]
{ #category : #accessing }
MultiPack >> repository: anObject [
]
{ #category : #accessing }
MultiPack >> requirements: aDictionary [
requirements := aDictionary
]

View File

@ -1,66 +1,66 @@
"
I'm run an implementation of the [Nano ID](https://github.com/ai/nanoid) tiny, secure URL-friendly unique string ID generator via its [Nim implementation](https://github.com/icyphox/nanoid.nim).
The Nim script has hard coded:
* a [base 58 encoding](https://medium.com/concerning-pharo/understanding-base58-encoding-23e673e37ff6) alphabet to avoid similar looking letter and the use of non-alphanumeric characters.
* a 12 characters length output, which gives [a pretty low probability collision](https://zelark.github.io/nano-id-cc/) for the previous alphabet:
~616 years needed, in order to have a 1% probability of at least one collision at a speed of 1000 IDs per hour.
This is more than enough for our unique IDs applications, mostly in the documentation context,
which consists of hand crafted and/or programmatically produced notes ,
for example in data narratives, book(lets) and TiddlyWiki tiddlers of tens or hundreds of notes at most,
unevenly produced between hours, days and/or weeks..
The `External` tag is related on its dependency on other programming languages and frameworks,
though the dependency should be loaded by just loading a small binary with no dependencies.
"
Class {
#name : #NanoID,
#superclass : #Object,
#category : #'ExoRepo-External'
}
{ #category : #accessing }
NanoID class >> binaryFile [
Smalltalk os isWindows
ifFalse: [ ^ MiniDocs appFolder / self scriptSourceCode basenameWithoutExtension ]
ifTrue: [ ^ ExoRepo userDataFolder / 'NanoId' / 'nanoid' ]
]
{ #category : #accessing }
NanoID class >> generate [
self binaryFile exists ifFalse: [ NanoID install].
Smalltalk os isWindows
ifTrue: [ ^ (LibC resultOfCommand:self binaryFile fullName) copyWithoutAll: (Character lf asString) ].
OSSUnixSubprocess new
command: self binaryFile fullName;
redirectStdout;
redirectStdout;
runAndWaitOnExitDo: [ :process :outString | ^ outString copyWithoutAll: (Character lf asString) ]
]
{ #category : #accessing }
NanoID class >> install [
"For the moment, only Gnu/Linux and Mac are supported.
IMPORTANT: Nimble, Nim's package manager should be installed, as this process doesn't verify its proper installation."
self binaryFile exists ifTrue: [ ^ MiniDocs appFolder ].
Nimble install: 'nanoid'.
Smalltalk os isWindows
ifTrue: [ ^ LibC resultOfCommand: 'nanoid c ',self scriptSourceCode fullName ].
OSSUnixSubprocess new
command: 'nim';
arguments: {'c'. self scriptSourceCode fullName};
runAndWaitOnExitDo: [ :process :outString |
(self scriptSourceCode parent / (self scriptSourceCode) basenameWithoutExtension) moveToPageTitled: MiniDocs appFolder asFileReference.
^ MiniDocs appFolder ]
]
{ #category : #accessing }
NanoID class >> isInstalled [
^ self binaryFile exists
]
{ #category : #accessing }
NanoID class >> scriptSourceCode [
^ FileLocator image parent / 'pharo-local/iceberg/Offray/MiniDocs/src/nanoIdGen.nim'
]
"
I'm run an implementation of the [Nano ID](https://github.com/ai/nanoid) tiny, secure URL-friendly unique string ID generator via its [Nim implementation](https://github.com/icyphox/nanoid.nim).
The Nim script has hard coded:
* a [base 58 encoding](https://medium.com/concerning-pharo/understanding-base58-encoding-23e673e37ff6) alphabet to avoid similar looking letter and the use of non-alphanumeric characters.
* a 12 characters length output, which gives [a pretty low probability collision](https://zelark.github.io/nano-id-cc/) for the previous alphabet:
~616 years needed, in order to have a 1% probability of at least one collision at a speed of 1000 IDs per hour.
This is more than enough for our unique IDs applications, mostly in the documentation context,
which consists of hand crafted and/or programmatically produced notes ,
for example in data narratives, book(lets) and TiddlyWiki tiddlers of tens or hundreds of notes at most,
unevenly produced between hours, days and/or weeks..
The `External` tag is related on its dependency on other programming languages and frameworks,
though the dependency should be loaded by just loading a small binary with no dependencies.
"
Class {
#name : #NanoID,
#superclass : #Object,
#category : #'ExoRepo-External'
}
{ #category : #accessing }
NanoID class >> binaryFile [
Smalltalk os isWindows
ifFalse: [ ^ MiniDocs appFolder / self scriptSourceCode basenameWithoutExtension ]
ifTrue: [ ^ ExoRepo userDataFolder / 'NanoId' / 'nanoid' ]
]
{ #category : #accessing }
NanoID class >> generate [
self binaryFile exists ifFalse: [ NanoID install].
Smalltalk os isWindows
ifTrue: [ ^ (LibC resultOfCommand:self binaryFile fullName) copyWithoutAll: (Character lf asString) ].
OSSUnixSubprocess new
command: self binaryFile fullName;
redirectStdout;
redirectStdout;
runAndWaitOnExitDo: [ :process :outString | ^ outString copyWithoutAll: (Character lf asString) ]
]
{ #category : #accessing }
NanoID class >> install [
"For the moment, only Gnu/Linux and Mac are supported.
IMPORTANT: Nimble, Nim's package manager should be installed, as this process doesn't verify its proper installation."
self binaryFile exists ifTrue: [ ^ MiniDocs appFolder ].
Nimble install: 'nanoid'.
Smalltalk os isWindows
ifTrue: [ ^ LibC resultOfCommand: 'nanoid c ',self scriptSourceCode fullName ].
OSSUnixSubprocess new
command: 'nim';
arguments: {'c'. self scriptSourceCode fullName};
runAndWaitOnExitDo: [ :process :outString |
(self scriptSourceCode parent / (self scriptSourceCode) basenameWithoutExtension) moveToPageTitled: MiniDocs appFolder asFileReference.
^ MiniDocs appFolder ]
]
{ #category : #accessing }
NanoID class >> isInstalled [
^ self binaryFile exists
]
{ #category : #accessing }
NanoID class >> scriptSourceCode [
^ FileLocator image parent / 'pharo-local/iceberg/Offray/MiniDocs/src/nanoIdGen.nim'
]

View File

@ -1,89 +1,89 @@
"
I'm a helper class modelling the common uses of the Nim's [Nimble package manager](https://github.com/nim-lang/nimble).
This was evolved in the context of the [Grafoscopio](mutabit.com/grafoscopio/en.html) community exploration and prototyping of interactive documentation.
"
Class {
#name : #Nimble,
#superclass : #Object,
#category : #'ExoRepo-External'
}
{ #category : #accessing }
Nimble class >> detect: packageName [
^ self installed
detect: [ :dependency | dependency beginsWith: packageName ]
ifFound: [ ^ true ]
ifNone: [ ^ false ]
]
{ #category : #accessing }
Nimble class >> install: packageName [
(self detect: packageName) ifTrue: [ ^ self ].
self installPackagesList.
Smalltalk os isWindows
ifTrue: [ ^ LibC runCommand: 'nimble install ', packageName ].
OSSUnixSubprocess new
command: 'nimble';
arguments: {'install'.
packageName};
redirectStdout;
redirectStderr;
runAndWaitOnExitDo: [ :process :outString :errString |
process isSuccess
ifTrue: [ Transcript show: 'Command exited correctly with output: ', outString. ]
ifFalse: [
^ 'Command exit with error status: ', process exitStatusInterpreter printString, String cr,
'Stderr contents: ', errString.
]
]
]
{ #category : #accessing }
Nimble class >> installPackagesList [
(FileLocator home / '.nimble' / 'packages_official.json') exists
ifTrue: [ ^ self ].
(Smalltalk os isUnix or: [ Smalltalk os isMacOS ])
ifTrue: [
OSSUnixSubprocess new
command: 'nimble';
arguments: #('refresh');
redirectStdout;
runAndWaitOnExitDo: [ :process :outString | ^ outString ].
].
Smalltalk os isWindows
ifTrue: [ ^ LibC resultOfCommand: 'nimble refresh' ]
]
{ #category : #accessing }
Nimble class >> installed [
Smalltalk os isWindows
ifTrue: [ | process |
process := GtExternalProcessBuilder new
command: 'nimble.exe';
args: #('list' '--installed');
output.
^ process stdout lines ].
OSSUnixSubprocess new
command: 'nimble';
arguments: #('list' '--installed');
redirectStdout;
redirectStderr;
runAndWaitOnExitDo: [ :process :outString :errString |
process isSuccess
ifTrue: [ ^ outString lines ];
ifFalse: [ ^ nil ]
]
]
{ #category : #accessing }
Nimble class >> version [
OSSUnixSubprocess new
command: 'nimble';
arguments: #('--version');
redirectStdout;
runAndWaitOnExitDo: [ :process :outString | ^ outString ]
]
"
I'm a helper class modelling the common uses of the Nim's [Nimble package manager](https://github.com/nim-lang/nimble).
This was evolved in the context of the [Grafoscopio](mutabit.com/grafoscopio/en.html) community exploration and prototyping of interactive documentation.
"
Class {
#name : #Nimble,
#superclass : #Object,
#category : #'ExoRepo-External'
}
{ #category : #accessing }
Nimble class >> detect: packageName [
^ self installed
detect: [ :dependency | dependency beginsWith: packageName ]
ifFound: [ ^ true ]
ifNone: [ ^ false ]
]
{ #category : #accessing }
Nimble class >> install: packageName [
(self detect: packageName) ifTrue: [ ^ self ].
self installPackagesList.
Smalltalk os isWindows
ifTrue: [ ^ LibC runCommand: 'nimble install ', packageName ].
OSSUnixSubprocess new
command: 'nimble';
arguments: {'install'.
packageName};
redirectStdout;
redirectStderr;
runAndWaitOnExitDo: [ :process :outString :errString |
process isSuccess
ifTrue: [ Transcript show: 'Command exited correctly with output: ', outString. ]
ifFalse: [
^ 'Command exit with error status: ', process exitStatusInterpreter printString, String cr,
'Stderr contents: ', errString.
]
]
]
{ #category : #accessing }
Nimble class >> installPackagesList [
(FileLocator home / '.nimble' / 'packages_official.json') exists
ifTrue: [ ^ self ].
(Smalltalk os isUnix or: [ Smalltalk os isMacOS ])
ifTrue: [
OSSUnixSubprocess new
command: 'nimble';
arguments: #('refresh');
redirectStdout;
runAndWaitOnExitDo: [ :process :outString | ^ outString ].
].
Smalltalk os isWindows
ifTrue: [ ^ LibC resultOfCommand: 'nimble refresh' ]
]
{ #category : #accessing }
Nimble class >> installed [
Smalltalk os isWindows
ifTrue: [ | process |
process := GtExternalProcessBuilder new
command: 'nimble.exe';
args: #('list' '--installed');
output.
^ process stdout lines ].
OSSUnixSubprocess new
command: 'nimble';
arguments: #('list' '--installed');
redirectStdout;
redirectStderr;
runAndWaitOnExitDo: [ :process :outString :errString |
process isSuccess
ifTrue: [ ^ outString lines ];
ifFalse: [ ^ nil ]
]
]
{ #category : #accessing }
Nimble class >> version [
OSSUnixSubprocess new
command: 'nimble';
arguments: #('--version');
redirectStdout;
runAndWaitOnExitDo: [ :process :outString | ^ outString ]
]

View File

@ -1,60 +1,56 @@
"
The `External` tag is related on its dependency on other programming languages and frameworks,
though the dependency should be loaded by just loading a small binary with no dependencies.
"
Class {
#name : #YQ,
#superclass : #Object,
#category : #'ExoRepo-External'
}
{ #category : #accessing }
YQ class >> binaryDownloadLinkFor: operativeSystem on: processor [
| binaryName binaryDownloadData |
binaryName := 'yq_', operativeSystem , '_', processor.
binaryDownloadData := ((self lastReleaseData at: 'assets')
select: [:each | (each at: 'name') beginsWith: binaryName ]) first.
^ binaryDownloadData at: 'browser_download_url'
]
{ #category : #accessing }
YQ class >> binaryFile [
"Starting with location on Arch Linux and its derivates. Multidistro and multiOS support should be added."
Smalltalk os isWindows ifFalse: [^ FileLocator root / 'usr/bin/yq'].
^ FileLocator home / 'scoop/shims/yq.exe'
]
{ #category : #accessing }
YQ class >> exoPackage [
^ ExoPackage new
repository: self repository
]
{ #category : #accessing }
YQ class >> install [
^ ExoPackage new
repository: self repository;
lastReleaseData
]
{ #category : #accessing }
YQ class >> jsonToYaml: aDictionary [
| jsonFile |
self binaryFile exists ifFalse: [ YQ install].
jsonFile := MarkupFile exportAsFileOn: FileLocator temp / 'data.json' containing: aDictionary.
(Smalltalk os isUnix or: [ Smalltalk os isMacOS ])
ifTrue: [
OSSUnixSubprocess new
shellCommand: 'cat ', jsonFile fullName,' | yq -y';
redirectStdout;
runAndWaitOnExitDo: [ :command :outString |
^ outString
]].
Smalltalk os isWindows
ifTrue: [ ^ LibC resultOfCommand: 'yq -p=json ', jsonFile fullName ].
]
{ #category : #accessing }
YQ class >> repository [
^ 'https://github.com/mikefarah/yq'
]
"
The `External` tag is related on its dependency on other programming languages and frameworks,
though the dependency should be loaded by just loading a small binary with no dependencies.
"
Class {
#name : #YQ,
#superclass : #Object,
#category : #'ExoRepo-External'
}
{ #category : #accessing }
YQ class >> binaryDownloadLinkFor: operativeSystem on: processor [
| binaryName binaryDownloadData |
binaryName := 'yq_', operativeSystem , '_', processor.
binaryDownloadData := ((self lastReleaseData at: 'assets')
select: [:each | (each at: 'name') beginsWith: binaryName ]) first.
^ binaryDownloadData at: 'browser_download_url'
]
{ #category : #accessing }
YQ class >> binaryFile [
"Starting with location on Arch Linux and its derivates. Multidistro and multiOS support should be added."
Smalltalk os isWindows ifFalse: [^ FileLocator root / 'usr/bin/yq'].
^ FileLocator home / 'scoop/shims/yq.exe'
]
{ #category : #accessing }
YQ class >> exoPackage [
^ ExoPackage new
repository: self repository
]
{ #category : #accessing }
YQ class >> install [
^ ExoPackage new
repository: self repository;
lastReleaseData
]
{ #category : #accessing }
YQ class >> jsonToYaml: aDictionary [
| jsonFile |
self binaryFile exists ifFalse: [ YQ install].
jsonFile := MarkupFile exportAsFileOn: FileLocator temp / 'data.json' containing: aDictionary.
Smalltalk os isWindows
ifTrue: [ ^ LibC resultOfCommand: 'yq -p=json ', jsonFile fullName ].
^ GtSubprocessWithInMemoryOutput new
shellCommand: 'cat ', jsonFile fullName,' | yq -y';
runAndWait;
stdout
]
{ #category : #accessing }
YQ class >> repository [
^ 'https://github.com/mikefarah/yq'
]

View File

@ -1 +1 @@
Package { #name : #ExoRepo }
Package { #name : #ExoRepo }