Skip to content

Commit

Permalink
Merge pull request #7 from Cognifide/hosts-editing
Browse files Browse the repository at this point in the history
Hosts editing more automated
  • Loading branch information
Krystian Panek authored Sep 23, 2020
2 parents 16333e4 + 8fceb3b commit 4d2a059
Show file tree
Hide file tree
Showing 9 changed files with 212 additions and 27 deletions.
7 changes: 6 additions & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ dependencies {
implementation(platform("org.jetbrains.kotlin:kotlin-bom"))
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")

implementation("com.cognifide.gradle:common-plugin:0.1.64")
implementation("com.cognifide.gradle:common-plugin:0.1.63")
implementation("org.buildobjects:jproc:2.3.0")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.5")
implementation("org.apache.commons:commons-lang3:3.9")
Expand Down Expand Up @@ -56,6 +56,11 @@ val check by tasks.getting(Task::class) {
}

tasks {
jar {
dependsOn(":hosts:jar")
from(provider { project(":hosts").tasks.getByName("jar") })
}

register<Jar>("sourcesJar") {
archiveClassifier.set("sources")
dependsOn("classes")
Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
version=1.0.9
version=1.1.0
release.useAutomaticVersion=true
30 changes: 30 additions & 0 deletions hosts/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
plugins {
kotlin("jvm")
application
java
}

description = "Hosts Editor"
version = ""

repositories {
jcenter()
}

dependencies {
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
}

application {
mainClass.set("MainKt")
}

tasks {
jar {
manifest {
attributes["Implementation-Title"] = project.description
attributes["Main-Class"] = "MainKt"
}
from(configurations.runtimeClasspath.get().files.map { if (it.isDirectory) it else zipTree(it) })
}
}
66 changes: 66 additions & 0 deletions hosts/src/main/kotlin/main.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import java.io.File

fun main(args: Array<String>) = try {
val sourceName = args.getOrNull(0) ?: "mysite"
val sourceFile = File(args.getOrNull(1) ?: ".gradle/environment/hosts.txt")
val sourceSection = Section(sourceName, sourceFile.readText().lines().map { it.trim() })
val targetFile = File(args.getOrNull(2) ?: "/etc/hosts")

val text = targetFile.readText()
val sections = Section.parseAll(text)

val targetSection = sections.find { it.name == sourceSection.name }

if (targetSection != null) {
targetFile.writeText(text.replace(targetSection.render(), sourceSection.render()))
} else {
targetFile.appendText("${System.lineSeparator()}${sourceSection.render()}")
}
} catch (e: Exception) {
println("Cannot update hosts file!")
println("Ensure using administrator/super-user privileges.")
println("Error details: ${e.message}")
}

data class Section(val name: String, val entries: List<String>) {
fun render() = mutableListOf<String>().apply {
add("#environment-start")
add("#name=$name")
addAll(entries)
add("#environment-end")
}.joinToString(System.lineSeparator())

companion object {
fun parseAll(text: String): List<Section> {
val sections = mutableListOf<Section>()

var section = false
var sectionName = ""
val sectionLines = mutableListOf<String>()

text.lineSequence().forEach { line ->
when (val l = line.trim()) {
"#environment-start" -> {
section = true
}
"#environment-end" -> {
sections.add(Section(sectionName, sectionLines.toList()))
section = false
sectionLines.clear()
}
else -> {
if (section) {
if (l.startsWith("#name=")) {
sectionName = l.substringAfter("#name=")
} else {
sectionLines.add(l)
}
}
}
}
}

return sections
}
}
}
2 changes: 2 additions & 0 deletions settings.gradle.kts
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
rootProject.name = "environment-plugin"

include(":hosts")
15 changes: 7 additions & 8 deletions src/main/kotlin/com/cognifide/gradle/environment/hosts/Host.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,7 @@ class Host(val url: String) {

val config = URL(url)

init {
if (url.isBlank()) {
throw EnvironmentException("Host URL cannot be blank!")
}
}

val text: String
get() = "$ip\t${config.host}"
val text: String get() = "$ip\t${config.host}"

fun tag(id: String) {
tags.add(id)
Expand All @@ -30,5 +23,11 @@ class Host(val url: String) {

fun tag(vararg ids: String) = tag(ids.asIterable())

init {
if (url.isBlank()) {
throw EnvironmentException("Host URL cannot be blank!")
}
}

override fun toString(): String = "Host(url='$url', ip='$ip', tags=$tags)"
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
package com.cognifide.gradle.environment.hosts

import com.cognifide.gradle.environment.EnvironmentExtension
import com.cognifide.gradle.common.utils.using
import com.cognifide.gradle.environment.EnvironmentException
import org.gradle.internal.os.OperatingSystem
import com.cognifide.gradle.environment.EnvironmentExtension
import java.io.Serializable

/**
* Manages host definitions in case of different purposes indicated by tags.
*/
class HostOptions(environment: EnvironmentExtension) : Serializable {
class HostOptions(private val environment: EnvironmentExtension) : Serializable {

val docker = environment.docker

Expand All @@ -22,17 +22,6 @@ class HostOptions(environment: EnvironmentExtension) : Serializable {
convention(common.obj.provider { docker.runtime.hostIp })
}

val osFile = common.obj.string {
convention(common.obj.provider {
when {
OperatingSystem.current().isWindows -> """C:\Windows\System32\drivers\etc\hosts"""
else -> "/etc/hosts"
}
})
}

val appendix: String get() = defined.get().joinToString("\n") { it.text }

operator fun String.invoke(options: Host.() -> Unit = {}) = define(this, options)

operator fun String.invoke(vararg tags: String) = define(this) { tag(tags.asIterable()) }
Expand Down Expand Up @@ -61,4 +50,10 @@ class HostOptions(environment: EnvironmentExtension) : Serializable {
fun all(tags: Iterable<String>) = defined.get().filter { h -> tags.all { t -> h.tags.contains(t) } }.ifEmpty {
throw EnvironmentException("Environment has no hosts tagged with '${tags.joinToString(",")}'!")
}

val updater by lazy { HostUpdater(environment) }

fun updater(options: HostUpdater.() -> Unit) = updater.using(options)

fun update() = updater.update()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package com.cognifide.gradle.environment.hosts

import com.cognifide.gradle.environment.EnvironmentExtension
import org.gradle.internal.os.OperatingSystem

class HostUpdater(val environment: EnvironmentExtension) {

private val project = environment.project

private val logger = project.logger

private val common = environment.common

val interactive = common.obj.boolean {
convention(true)
common.prop.boolean("environment.hosts.updater.interactive")?.let { set(it) }
}

val workDir = common.obj.dir {
convention(environment.rootDir.dir("hosts"))
common.prop.file("environment.hosts.updater.workDir")?.let { set(it) }
}

val targetFile = common.obj.file {
fileProvider(common.obj.provider {
project.file(when {
OperatingSystem.current().isWindows -> """C:\Windows\System32\drivers\etc\hosts"""
else -> "/etc/hosts"
})
})
common.prop.file("environment.hosts.updater.targetFile")?.let { set(it) }
}

val section = common.obj.string {
convention(environment.docker.stack.internalName)
common.prop.string("environment.hosts.updater.section")?.let { set(it) }
}

@Suppress("MaxLineLength")
fun update() {
val os = OperatingSystem.current()
val osFile = targetFile.get()

val dir = workDir.get().asFile.apply { mkdirs() }

val entriesFile = dir.resolve("hosts.txt").apply {
logger.info("Generating hosts entries file: $this")
writeText(environment.hosts.defined.get().joinToString(System.lineSeparator()) { it.text })
}
val updaterJar = dir.resolve("hosts.jar").apply {
logger.info("Providing hosts updater program: $this")
outputStream().use { output ->
this@HostUpdater.javaClass.getResourceAsStream("/hosts.jar").use {
input -> input.copyTo(output)
}
}
}
val sectionName = section.get()

if (os.isWindows && interactive.get()) {
val scriptFile = dir.resolve("hosts.bat")
logger.info("Generating hosts updating script: $scriptFile")

scriptFile.writeText("""
powershell -command "Start-Process cmd -ArgumentList '/C cd %CD% && java -jar $updaterJar $sectionName $entriesFile $osFile' -Verb runas"
""".trimIndent())
project.exec { it.commandLine("cmd", "/C", scriptFile.toString()) }
logger.lifecycle("Environment hosts successfully updated.")
} else {
val scriptFile = dir.resolve("hosts.sh")
logger.info("Generating hosts updating script: $scriptFile")

if (os.isMacOsX && interactive.get()) {
scriptFile.writeText("""
#!/bin/sh
osascript -e "do shell script \"java -jar $updaterJar $sectionName $entriesFile $osFile\" with prompt \"Gradle Environment Hosts\" with administrator privileges"
""".trimIndent())
project.exec { it.commandLine("sh", scriptFile.toString()) }
logger.lifecycle("Environment hosts successfully updated.")
} else {
scriptFile.writeText("""
#!/bin/sh
java -jar $updaterJar $sectionName $entriesFile $osFile
""".trimIndent())
logger.lifecycle("To update environment hosts, run script below as administrator/super-user:\n$scriptFile")
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,11 @@ open class EnvironmentHosts : EnvironmentDefaultTask() {

@TaskAction
fun appendHosts() {
logger.lifecycle("Hosts entries to be appended to ${environment.hosts.osFile.get()}:")
logger.quiet(environment.hosts.appendix)
environment.hosts.update()
}

init {
description = "Prints environment hosts entries."
description = "Updates environment hosts entries."
}

companion object {
Expand Down

0 comments on commit 4d2a059

Please sign in to comment.