gwen.gpm.process.GPMOperations.scala Maven / Gradle / Ivy
/*
* Copyright 2017 Branko Juric, Brady Wood
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package gwen.gpm.process
import java.io.File
import java.net.URI
import gwen.gpm.Errors._
import gwen.gpm.GPMChecksumNotConfiguredException
import gwen.gpm.model.{GPackage, OSType, Operation}
import gwen.gpm.process.FileIO.FileExtensions
import scala.io.Source
import scala.sys.process._
import scala.util.{Failure, Try}
/**
* Performs all Gwen package management operations.
*
* @param options the command line options
* @param settings the property settings
*/
class GPMOperations(options: GPMOptions, settings: GPMSettings) {
private val rootDir = new File(s"${FileIO.userHomeDir.getPath}/.gwen")
private val lockFile = init(options)
private val cacheDir = new File(s"$rootDir/cache")
private val packageDir = new File(s"$rootDir/package")
private val version = resolveVersion(options)
private val packageId = s"${options.pkg.name}-$version"
private val checksumKey = s"gwen.checksum.$packageId"
private val archiveFile = new File(cacheDir, s"${options.pkg.name}/$packageId.${options.pkg.archiveType.fileExtension}")
private val destinationDir = options.destination.getOrElse(new File(packageDir, options.pkg.name))
private val installFile = new File(destinationDir, s".gwen/$packageId")
private def init(options: GPMOptions): File = {
// create or wait for lock file (only one process at a time allowed to work in .gwen folder)
val lockFile = new File(s"${rootDir.getPath}/.lock")
if (lockFile.exists()) {
println("[gwen-gpm] Waiting for another process to release the ~/.gwen/.lock file (delete it manually to force resume)..")
while(lockFile.exists()) Thread.sleep(1000)
println("[gwen-gpm] ..lock file released")
}
if (!lockFile.getParentFile.exists()) lockFile.getParentFile.mkdirs()
lockFile.createNewFile()
lockFile.deleteOnExit()
lockFile
}
def resolveVersion(options: GPMOptions): String = {
val targetVersion = settings.getOpt(options.version).getOrElse(options.version)
println(s"[gwen-gpm] Target ${options.pkg} version is $targetVersion")
val targetLatest = targetVersion == "latest"
val latestVersionFile = new File(cacheDir, s"${options.pkg.name}/${options.pkg.name}.latest")
if (options.operation == Operation.update || options.operation == Operation.download || (targetLatest && !latestVersionFile.exists())) {
val latestVersion = options.pkg.fetchLatestVersion
if (targetVersion != latestVersion) {
println(s"[gwen-gpm] The latest available ${options.pkg} version is $latestVersion")
}
if ((targetLatest || targetVersion == latestVersion) && options.pkg.name.contains("gwen")) {
println(s"[gwen-gpm] You'll be using the latest ${options.pkg} ..Very good!")
}
if (!latestVersionFile.getParentFile.exists()) latestVersionFile.getParentFile.mkdirs()
latestVersionFile.writeText(latestVersion)
if (targetLatest) latestVersion else targetVersion
} else if (targetLatest) {
if (latestVersionFile.exists()) {
val latestVersion = Source.fromFile(latestVersionFile).mkString.trim
if (targetVersion != latestVersion) {
println(s"[gwen-gpm] The latest cached ${options.pkg} version is $latestVersion")
}
latestVersion
} else {
latestVersionResolveError(options.pkg.name)
}
} else {
targetVersion
}
}
def install(): File = {
try {
download()
if (destinationDir.exists()) {
deleteExisting()
}
installArchive()
destinationDir
} finally {
lockFile.delete()
}
}
def download(): File = {
if (!archiveFile.exists()) {
val downloadUrl = options.pkg.getDownloadUrl(version)
println(s"[gwen-gpm] Downloading $packageId from $downloadUrl")
if (!archiveFile.getParentFile.exists()) archiveFile.getParentFile.mkdirs()
val archiveURL = new URI(downloadUrl)
val (_, fileChecksum) = archiveFile.download(archiveURL.toURL, settings)
verifyChecksum(settings, options, fileChecksum)
println(s"[gwen-gpm] Download done")
} else {
println(s"[gwen-gpm] $packageId exists in download cache")
}
archiveFile
}
private def verifyChecksum(settings: GPMSettings, options: GPMOptions, fileChecksum: String) = {
def getChecksums(download: Boolean): Option[List[String]] =
settings.loadChecksums(download).map(_.get(checksumKey).map(_.split(",").toList)).getOrElse(None)
val verify = options.version != "latest" || sys.props.get("gpm.checksum.latest").getOrElse("false").toBoolean
getChecksums(download = false).getOrElse(getChecksums(download = true).getOrElse(Nil)) match {
case Nil =>
if (verify) {
archiveFile.delete()
checksumNotConfiguredError(checksumKey, options.pkg.getDownloadUrl(version))
}
case checksums =>
if (!checksums.exists(_.equalsIgnoreCase(fileChecksum))) {
if (verify) {
archiveFile.delete()
invalidChecksumError()
}
} else {
println(s"[gwen-gpm] Checksum OK")
}
}
}
private def installArchive(): Option[File] = {
if (!destinationDir.exists()) {
println(s"[gwen-gpm] Installing $packageId to ${destinationDir.maskUserHomeDir}")
unpackArchive()
println("[gwen-gpm] Install done")
Some(destinationDir)
} else {
println(s"[gwen-gpm] $packageId already installed in ${destinationDir.maskUserHomeDir}")
None
}
}
private def unpackArchive() = {
// verify checksum of archive first (reattempt download on failure)
Try(verifyChecksum(settings, options, archiveFile.checksum)) match {
case Failure(e) =>
if (e.isInstanceOf[GPMChecksumNotConfiguredException]) throw e
else {
println(s"[gwen-gpm] ${e.getMessage}")
println(s"[gwen-gpm] Attempting download one more time")
download()
}
case _ => // OK
}
destinationDir.mkdirs()
// unpack the archive
if (archiveFile.getName.endsWith(".zip")) archiveFile.unpackZip(destinationDir, options.pkg.excludeTopDir)
else archiveFile.unpackTarGz(destinationDir, options.pkg.excludeTopDir)
// move all files up if unpacked contents consist of a single directory having the same name as the package Id
destinationDir.listFiles().toList match {
case dir :: Nil if dir.isDirectory && dir.getName == packageId =>
dir.moveFiles(destinationDir)
dir.delete()
case _ => // noop
}
// create a package install file in hidden .gwen folder
if (!installFile.getParentFile.exists()) installFile.getParentFile.mkdirs()
installFile.createNewFile()
// set execution permission on non windows platforms
if (OSType.determine() != OSType.Win) {
Seq("chmod", "-R", "u+x", destinationDir.getAbsolutePath).!
}
}
private def deleteExisting() = {
val hiddenGwenDir = new File(destinationDir, ".gwen")
if (!hiddenGwenDir.exists()) {
cannotInstallToExternallyManagedDir(packageId, destinationDir)
} else {
Option(hiddenGwenDir.listFiles()) match {
case Some(files) =>
files.map(_.getName).find(name => GPackage.values.exists(pkg => name.startsWith(pkg.name))) match {
case Some(existingPkg) =>
if (!existingPkg.startsWith(options.pkg.name)) {
cannotOverwriteDifferentPackage(packageId, existingPkg, destinationDir)
}
if(!existingPkg.endsWith(version)) {
println(s"[gwen-gpm] Deleting $existingPkg installation at: ${destinationDir.maskUserHomeDir}")
destinationDir.deleteDir()
println("[gwen-gpm] Delete done")
}
case None => cannotInstallToTamperedDir(packageId, destinationDir)
}
case None => cannotInstallToTamperedDir(packageId, destinationDir)
}
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy