diff --git a/.gitignore b/.gitignore index 5312ec8..164fffb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ .vagrant/ .idea/ provision/provision-nexus/sources/ +shared/ diff --git a/README.md b/README.md index 5e781f0..9becf66 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,7 @@ This will: * Configure Nexus through Groovy scripts. * Create the `adhoc-package` repository. * Create the `npm-group`, `npm-hosted` and `npmjs.org-proxy` repositories. + * Create the `chocolatey-group`, `chocolatey-hosted` and `chocolatey.org-proxy` repositories. * Configure the NuGet `nuget-hosted` repository to accept pushing with an API key. * Schedule a task to remove the old snapshots from the `maven-snapshots` repository. * Create users and a custom `deployer` role. @@ -25,6 +26,8 @@ This will: Build and install the [Ubuntu Base Box](https://github.com/rgl/ubuntu-vagrant). +Build and install the [Windows Base Box](https://github.com/rgl/windows-2016-vagrant). + Add the following entry to your `/etc/hosts` file: ``` diff --git a/Vagrantfile b/Vagrantfile index f5284b8..e808c1d 100644 --- a/Vagrantfile +++ b/Vagrantfile @@ -1,14 +1,32 @@ +nexus_domain = 'nexus.example.com' +nexus_ip = '192.168.56.3' + Vagrant.configure(2) do |config| - config.vm.box = 'ubuntu-16.04-amd64' - config.vm.hostname = 'nexus.example.com' - config.vm.network 'private_network', ip: '192.168.56.3' config.vm.provider 'virtualbox' do |vb| vb.linked_clone = true vb.cpus = 2 - vb.memory = 4096 + vb.memory = 2048 + end + + config.vm.define :nexus do |config| + config.vm.box = 'ubuntu-16.04-amd64' + config.vm.hostname = nexus_domain + config.vm.network 'private_network', ip: nexus_ip + config.vm.provision :shell, path: 'provision/provision-base.sh' + config.vm.provision :shell, path: 'provision/provision-nexus.sh' + config.vm.provision :shell, path: 'provision/test.sh' + config.vm.provision :shell, path: 'provision/summary.sh' + end + + config.vm.define :windows do |config| + config.vm.box = 'windows-2016-amd64' + config.vm.network 'private_network', ip: '192.168.56.4' + config.vm.provider :virtualbox do |v, override| + v.customize ['modifyvm', :id, '--vram', 64] + v.customize ['modifyvm', :id, '--clipboard', 'bidirectional'] + end + config.vm.provision :shell, inline: "echo '#{nexus_ip} #{nexus_domain}' | Out-File -Encoding Ascii -Append c:/Windows/System32/drivers/etc/hosts" + config.vm.provision :shell, path: 'provision/windows/ps.ps1', args: ['provision-base.ps1', nexus_domain] + config.vm.provision :shell, path: 'provision/windows/ps.ps1', args: ['use-chocolatey-repository.ps1', nexus_domain] end - config.vm.provision :shell, path: 'provision/provision-base.sh' - config.vm.provision :shell, path: 'provision/provision-nexus.sh' - config.vm.provision :shell, path: 'provision/test.sh' - config.vm.provision :shell, path: 'provision/summary.sh' end diff --git a/provision/provision-base.sh b/provision/provision-base.sh index 557c7b1..91a3cb5 100644 --- a/provision/provision-base.sh +++ b/provision/provision-base.sh @@ -58,6 +58,13 @@ openssl x509 -req -sha256 \ -days 365 \ -in $config_fqdn-csr.pem \ -out $config_fqdn-crt.pem +openssl x509 \ + -in $config_fqdn-crt.pem \ + -outform der \ + -out $config_fqdn-crt.der +# copy the certificate to a place where it can be used by other machines. +mkdir -p /vagrant/shared +cp $config_fqdn-crt.* /vagrant/shared popd diff --git a/provision/provision-nexus/src/main/groovy/provision.groovy b/provision/provision-nexus/src/main/groovy/provision.groovy index 595e554..2302e11 100644 --- a/provision/provision-nexus/src/main/groovy/provision.groovy +++ b/provision/provision-nexus/src/main/groovy/provision.groovy @@ -1,8 +1,9 @@ -// run this file inside the Vagrant environment with bash /vagrant/execute-provision.groovy-script.sh +// run this file inside the Vagrant environment with bash /vagrant/provision/execute-provision.groovy-script.sh // see https://help.sonatype.com/display/NXRM3/REST+and+Integration+API // see https://github.com/sonatype/nexus-book-examples/tree/nexus-3.x/scripting/nexus-script-example import groovy.json.JsonOutput +import org.sonatype.nexus.repository.storage.WritePolicy import org.sonatype.nexus.security.user.UserSearchCriteria import org.sonatype.nexus.security.authc.apikey.ApiKeyStore import org.sonatype.nexus.security.realm.RealmManager @@ -29,6 +30,15 @@ repository.createNpmProxy("npmjs.org-proxy", "https://registry.npmjs.org", "defa repository.createNpmGroup("npm-group", ["npm-hosted", "npmjs.org-proxy"], "default") +// create a chocolatey repository backed by the default blob store. +repository.createNugetHosted("chocolatey-hosted", "default", true, WritePolicy.ALLOW_ONCE) +// create a chocolatey proxy repository backed by the default blob store. +// see https://help.sonatype.com/display/NXRM3/.NET+Package+Repositories+with+NuGet +repository.createNugetProxy("chocolatey.org-proxy", "https://chocolatey.org/api/v2/", "default") +// create a chocolatey group repository that merges the chocolatey-host and chocolatey.org-proxy together. +repository.createNugetGroup("chocolatey-group", ["chocolatey-hosted", "chocolatey.org-proxy"], "default") + + // see http://stackoverflow.com/questions/8138164/groovy-generate-random-string-from-given-character-set def random(String alphabet, int n) { new Random().with { diff --git a/provision/test.sh b/provision/test.sh index 6daf210..018e75c 100644 --- a/provision/test.sh +++ b/provision/test.sh @@ -23,6 +23,7 @@ function nuget { nuget_source_url=http://localhost:8081/repository/nuget-group/ nuget_source_push_url=http://localhost:8081/repository/nuget-hosted/ nuget_source_push_api_key=$(nexus-groovy get-jenkins-nuget-api-key | jq -r '.result | fromjson | .apiKey') +echo -n $nuget_source_push_api_key >/vagrant/shared/jenkins-nuget-api-key # test installing a package from the public NuGet repository. nuget install MsgPack -Source $nuget_source_url diff --git a/provision/windows/GoogleChrome-external_extensions.json b/provision/windows/GoogleChrome-external_extensions.json new file mode 100644 index 0000000..3703272 --- /dev/null +++ b/provision/windows/GoogleChrome-external_extensions.json @@ -0,0 +1,10 @@ +{ + // JSON Formatter (https://chrome.google.com/webstore/detail/json-formatter/bcjindcccaagfpapjjmafapmmgkkhgoa). + "bcjindcccaagfpapjjmafapmmgkkhgoa": { + "external_update_url": "https://clients2.google.com/service/update2/crx" + }, + // uBlock Origin (https://chrome.google.com/webstore/detail/ublock-origin/cjpalhdlnbpafiamejdnhcphjbkeiagm). + "cjpalhdlnbpafiamejdnhcphjbkeiagm": { + "external_update_url": "https://clients2.google.com/service/update2/crx" + } +} \ No newline at end of file diff --git a/provision/windows/GoogleChrome-master_bookmarks.html b/provision/windows/GoogleChrome-master_bookmarks.html new file mode 100644 index 0000000..3dd16a8 --- /dev/null +++ b/provision/windows/GoogleChrome-master_bookmarks.html @@ -0,0 +1,10 @@ + + +
+
+
+
diff --git a/provision/windows/GoogleChrome-master_preferences.json b/provision/windows/GoogleChrome-master_preferences.json
new file mode 100644
index 0000000..50a5896
--- /dev/null
+++ b/provision/windows/GoogleChrome-master_preferences.json
@@ -0,0 +1,34 @@
+{
+ "session": {
+ "restore_on_startup": 1
+ },
+ "bookmark_bar": {
+ "show_on_all_tabs": true
+ },
+ "sync_promo": {
+ "show_on_first_run_allowed": false
+ },
+ "distribution": {
+ "import_bookmarks_from_file": "C:\\Program Files (x86)\\Google\\Chrome\\Application\\master_bookmarks.html",
+ "import_bookmarks": true,
+ "import_history": true,
+ "import_home_page": true,
+ "import_search_engine": true,
+ "suppress_first_run_bubble": true,
+ "do_not_create_desktop_shortcut": true,
+ "do_not_create_quick_launch_shortcut": false,
+ "do_not_launch_chrome": true,
+ "do_not_register_for_update_launch": true,
+ "make_chrome_default": true,
+ "make_chrome_default_for_user": true,
+ "suppress_first_run_default_browser_prompt": false,
+ "system_level": true,
+ "verbose_logging": true
+ },
+ "first_run_tabs": [
+ "chrome://extensions",
+ "chrome://version",
+ "https://github.com/rgl",
+ "https://ruilopes.com"
+ ]
+}
\ No newline at end of file
diff --git a/provision/windows/provision-base.ps1 b/provision/windows/provision-base.ps1
new file mode 100644
index 0000000..2be5656
--- /dev/null
+++ b/provision/windows/provision-base.ps1
@@ -0,0 +1,38 @@
+param(
+ [string]$nexusDomain = 'nexus.example.com'
+)
+
+# set keyboard layout.
+# NB you can get the name from the list:
+# [System.Globalization.CultureInfo]::GetCultures('InstalledWin32Cultures') | out-gridview
+Set-WinUserLanguageList pt-PT -Force
+
+# set the date format, number format, etc.
+Set-Culture pt-PT
+
+# set the timezone.
+# tzutil /l lists all available timezone ids
+& $env:windir\system32\tzutil /s "GMT Standard Time"
+
+# show window content while dragging.
+Set-ItemProperty -Path 'HKCU:Control Panel\Desktop' -Name DragFullWindows -Value 1
+
+# show hidden files.
+Set-ItemProperty -Path HKCU:Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced -Name Hidden -Value 1
+
+# show file extensions.
+Set-ItemProperty -Path HKCU:Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced -Name HideFileExt -Value 0
+
+# display full path in the title bar.
+New-Item -Path HKCU:Software\Microsoft\Windows\CurrentVersion\Explorer\CabinetState -Force `
+ | New-ItemProperty -Name FullPath -Value 1 -PropertyType DWORD `
+ | Out-Null
+
+# set default Explorer location to This PC.
+Set-ItemProperty -Path HKCU:SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced -Name LaunchTo -Value 1
+
+# trust the nexus server certificate.
+Import-Certificate `
+ -FilePath "c:/vagrant/shared/$nexusDomain-crt.der" `
+ -CertStoreLocation Cert:\LocalMachine\Root `
+ | Out-Null
diff --git a/provision/windows/ps.ps1 b/provision/windows/ps.ps1
new file mode 100644
index 0000000..1532f6a
--- /dev/null
+++ b/provision/windows/ps.ps1
@@ -0,0 +1,47 @@
+param(
+ [Parameter(Mandatory=$true)]
+ [String]$script,
+ [Parameter(ValueFromRemainingArguments=$true)]
+ [String[]]$scriptArguments
+)
+
+Set-StrictMode -Version Latest
+
+$ErrorActionPreference = 'Stop'
+
+trap {
+ Write-Output "ERROR: $_"
+ Write-Output (($_.ScriptStackTrace -split '\r?\n') -replace '^(.*)$','ERROR: $1')
+ Write-Output (($_.Exception.ToString() -split '\r?\n') -replace '^(.*)$','ERROR EXCEPTION: $1')
+ Exit 1
+}
+
+# wrap the choco command (to make sure this script aborts when it fails).
+function Start-Choco([string[]]$Arguments, [int[]]$SuccessExitCodes=@(0)) {
+ $command, $commandArguments = $Arguments
+ if ($command -eq 'install') {
+ $Arguments = @($command, '--no-progress') + $commandArguments
+ }
+ for ($n = 0; $n -lt 10; ++$n) {
+ if ($n) {
+ # NB sometimes choco fails with "The package was not found with the source(s) listed."
+ # but normally its just really a transient "network" error.
+ Write-Host "Retrying choco install..."
+ Start-Sleep -Seconds 3
+ }
+ &C:\ProgramData\chocolatey\bin\choco.exe @Arguments
+ if ($SuccessExitCodes -Contains $LASTEXITCODE) {
+ return
+ }
+ }
+ throw "$(@('choco')+$Arguments | ConvertTo-Json -Compress) failed with exit code $LASTEXITCODE"
+}
+function choco {
+ Start-Choco $Args
+}
+
+cd c:/vagrant/provision/windows
+$script = Resolve-Path $script
+cd (Split-Path $script -Parent)
+Write-Host "Running $script..."
+. $script @scriptArguments
diff --git a/provision/windows/use-chocolatey-repository.ps1 b/provision/windows/use-chocolatey-repository.ps1
new file mode 100644
index 0000000..6975231
--- /dev/null
+++ b/provision/windows/use-chocolatey-repository.ps1
@@ -0,0 +1,68 @@
+param(
+ [string]$nexusDomain = 'nexus.example.com'
+)
+
+# install chocolatey.
+iex ((New-Object Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))
+
+Write-Host 'Default Chocolatey sources:'
+choco sources list
+
+# see https://github.com/chocolatey/choco/wiki/CommandsSources
+Write-Host 'Configuring chocolatey to use the nexus server...'
+choco sources remove --name chocolatey
+choco sources add --name nexus --source https://$nexusDomain/repository/chocolatey-group/
+
+Write-Host 'Current Chocolatey sources:'
+choco sources list
+
+Write-Host 'Installing Google Chrome from the nexus server...'
+# NB --ignore-checksums is needed because chrome does not release a versioned
+# installer... as such, sometimes this package installation breaks if we
+# do not ignore the checksums and there's a new chrome version available.
+# see https://www.chromium.org/administrators/configuring-other-preferences
+choco install -y --ignore-checksums googlechrome
+$chromeLocation = 'C:\Program Files (x86)\Google\Chrome\Application'
+cp -Force GoogleChrome-external_extensions.json (Get-Item "$chromeLocation\*\default_apps\external_extensions.json").FullName
+cp -Force GoogleChrome-master_preferences.json "$chromeLocation\master_preferences"
+cp -Force GoogleChrome-master_bookmarks.html "$chromeLocation\master_bookmarks.html"
+
+# see https://github.com/chocolatey/choco/wiki/CreatePackages
+# see https://docs.nuget.org/docs/reference/nuspec-reference
+Write-Host 'Creating the graceful-terminating-console-application-windows chocolatey package...'
+Push-Location $env:TEMP
+mkdir graceful-terminating-console-application-windows | Out-Null
+cd graceful-terminating-console-application-windows
+Set-Content -Encoding Ascii graceful-terminating-console-application-windows.nuspec @'
+