Skip to content

Commit

Permalink
feat: support windows and projects with no mill
Browse files Browse the repository at this point in the history
This change adds in the ability to download a millw to validate the
targets with if no mill is found in the project root. This also adds in
support for Windows and Windows into the CI matrix.
  • Loading branch information
ckipp01 committed Aug 15, 2022
1 parent d7d9f47 commit baf91b8
Show file tree
Hide file tree
Showing 10 changed files with 197 additions and 5 deletions.
14 changes: 13 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,12 @@ jobs:
./mill -i __.fix --check

test:
runs-on: 'ubuntu-latest'
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
java: ['8', '17']
os: ['ubuntu-latest', 'windows-latest']

steps:
- uses: actions/checkout@v3
Expand All @@ -49,10 +50,21 @@ jobs:
- name: Compile
run:
./mill -i __.compile
if: matrix.os == 'ubuntu-latest'

- name: Compile
run: ./millw.bat -i __.compile
if: matrix.os == 'windows-latest'

- name: Test
run:
./mill -i --debug itest
if: matrix.os == 'ubuntu-latest'

- name: Test
run:
./millw -i --debug itest
if: matrix.os == 'windows-latest'

publish-sonatype:
if: github.repository == 'ckipp01/mill-giter8' && contains(github.ref, 'refs/tags/')
Expand Down
5 changes: 5 additions & 0 deletions build.sc
Original file line number Diff line number Diff line change
Expand Up @@ -68,13 +68,18 @@ object itest extends MillIntegrationTestModule {

override def pluginsUnderTest = Seq(plugin)

override def perTestResources = T.sources { millSourcePath / "shared" }

def testBase = millSourcePath / "src"

override def testInvocations: T[Seq[(PathRef, Seq[TestInvocation.Targets])]] =
T {
Seq(
PathRef(testBase / "minimal") -> Seq(
TestInvocation.Targets(Seq("g8.validate"), noServer = true)
),
PathRef(testBase / "no-mill") -> Seq(
TestInvocation.Targets(Seq("g8.validate"), noServer = true)
)
)
}
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Empty file added itest/src/no-mill/.gitkeep
Empty file.
115 changes: 115 additions & 0 deletions millw.bat
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
@echo off

rem This is a wrapper script, that automatically download mill from GitHub release pages
rem You can give the required mill version with --mill-version parameter
rem If no version is given, it falls back to the value of DEFAULT_MILL_VERSION
rem
rem Project page: https://github.com/lefou/millw
rem Script Version: 0.4.2
rem
rem If you want to improve this script, please also contribute your changes back!
rem
rem Licensed under the Apache License, Version 2.0

rem setlocal seems to be unavailable on Windows 95/98/ME
rem but I don't think we need to support them in 2019
setlocal enabledelayedexpansion

set "DEFAULT_MILL_VERSION=0.10.0"

set "MILL_REPO_URL=https://github.com/com-lihaoyi/mill"

rem %~1% removes surrounding quotes
if [%~1%]==[--mill-version] (
rem shift command doesn't work within parentheses
if not [%~2%]==[] (
set MILL_VERSION=%~2%
set "STRIP_VERSION_PARAMS=true"
) else (
echo You specified --mill-version without a version. 1>&2
echo Please provide a version that matches one provided on 1>&2
echo %MILL_REPO_URL%/releases 1>&2
exit /b 1
)
)

if [!MILL_VERSION!]==[] (
if exist .mill-version (
set /p MILL_VERSION=<.mill-version
)
)

if [!MILL_VERSION!]==[] (
set MILL_VERSION=%DEFAULT_MILL_VERSION%
)

set MILL_DOWNLOAD_PATH=%USERPROFILE%\.mill\download

rem without bat file extension, cmd doesn't seem to be able to run it
set MILL=%MILL_DOWNLOAD_PATH%\!MILL_VERSION!.bat

if not exist "%MILL%" (
set VERSION_PREFIX=%MILL_VERSION:~0,4%
set DOWNLOAD_SUFFIX=-assembly
if [!VERSION_PREFIX!]==[0.0.] set DOWNLOAD_SUFFIX=
if [!VERSION_PREFIX!]==[0.1.] set DOWNLOAD_SUFFIX=
if [!VERSION_PREFIX!]==[0.2.] set DOWNLOAD_SUFFIX=
if [!VERSION_PREFIX!]==[0.3.] set DOWNLOAD_SUFFIX=
if [!VERSION_PREFIX!]==[0.4.] set DOWNLOAD_SUFFIX=
set VERSION_PREFIX=

for /F "delims=- tokens=1" %%A in ("!MILL_VERSION!") do set MILL_VERSION_BASE=%%A
for /F "delims=- tokens=2" %%A in ("!MILL_VERSION!") do set MILL_VERSION_MILESTONE=%%A
set VERSION_MILESTONE_START=!MILL_VERSION_MILESTONE:~0,1!
if [!VERSION_MILESTONE_START!]==[M] (
set MILL_VERSION_TAG="!MILL_VERSION_BASE!-!MILL_VERSION_MILESTONE!"
) else (
set MILL_VERSION_TAG=!MILL_VERSION_BASE!
)

rem there seems to be no way to generate a unique temporary file path (on native Windows)
set DOWNLOAD_FILE=%MILL%.tmp

set DOWNLOAD_URL=%MILL_REPO_URL%/releases/download/!MILL_VERSION_TAG!/!MILL_VERSION!!DOWNLOAD_SUFFIX!

echo Downloading mill %MILL_VERSION% from %MILL_REPO_URL%/releases ... 1>&2

if not exist "%MILL_DOWNLOAD_PATH%" mkdir "%MILL_DOWNLOAD_PATH%"
rem curl is bundled with recent Windows 10
rem but I don't think we can expect all the users to have it in 2019
where /Q curl
if %ERRORLEVEL% EQU 0 (
curl -f -L "!DOWNLOAD_URL!" -o "!DOWNLOAD_FILE!"
) else (
rem bitsadmin seems to be available on Windows 7
rem without /dynamic, github returns 403
rem bitsadmin is sometimes needlessly slow but it looks better with /priority foreground
bitsadmin /transfer millDownloadJob /dynamic /priority foreground "!DOWNLOAD_URL!" "!DOWNLOAD_FILE!"
)
if not exist "!DOWNLOAD_FILE!" (
echo Could not download mill %MILL_VERSION% 1>&2
exit /b 1
)

move /y "!DOWNLOAD_FILE!" "%MILL%"

set DOWNLOAD_FILE=
set DOWNLOAD_SUFFIX=
)

set MILL_DOWNLOAD_PATH=
set MILL_VERSION=
set MILL_REPO_URL=

set MILL_PARAMS=%*

if defined STRIP_VERSION_PARAMS (
for /f "tokens=1-2*" %%a in ("%*") do (
rem strip %%a - It's the "--mill-version" option.
rem strip %%b - it's the version number that comes after the option.
rem keep %%c - It's the remaining options.
set MILL_PARAMS=%%c
)
)

"%MILL%" %MILL_PARAMS%
68 changes: 64 additions & 4 deletions plugin/src/io/kipp/mill/giter8/G8Module.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import mill.define.Target
import mill.define.TaskModule
import os.Path

import java.nio.file.attribute.PosixFilePermission
import scala.util.Failure
import scala.util.Success
import scala.util.Using
Expand Down Expand Up @@ -46,6 +47,55 @@ trait G8Module extends TaskModule {
*/
def validationTargets: Target[Seq[String]] = T { Seq.empty[String] }

/** If no mill is detected in the template in order to run the
* [[io.kipp.mill.giter8.G8Module.validationTargets]] aginst it we need Mill.
* To make this easier, we just download millw, cache it, and use it.
*
* @return
* the PathRef to millw
*/
private def downloadMill: T[PathRef] = T.task {
val log = T.log
val exeSuffix = if (scala.util.Properties.isWin) ".bat" else ""
log.info(
s"No mill found in template root, so falling back to millw${exeSuffix} to validate targets."
)

val url =
s"https://raw.githubusercontent.com/lefou/millw/main/millw${exeSuffix}"

val cacheDir = T.env
.get("XDG_CACHE_HOME")
.map(os.Path(_))
.getOrElse(
os.home / ".cache"
) / "mill-giter8"

val cacheTarget = cacheDir / s"millw${exeSuffix}"

os.makeDir.all(cacheDir)

if (!os.exists(cacheTarget)) {
log.info(
s"No millw${exeSuffix} found in cache so downloading one for you"
)

val r = requests.get(url)
if (r.is2xx) {
os.write(cacheTarget, r.bytes)
if (!scala.util.Properties.isWin) {
os.perms.set(
cacheTarget,
os.perms(cacheTarget) + PosixFilePermission.OWNER_EXECUTE
)
}
} else {
Result.Failure(s"Unable to download millw${exeSuffix} when needed.")
}
}
PathRef(cacheTarget)
}

/** The main target that you'll want to use. This will first ensure that
* [[io.kipp.mill.giter8.G8Module.generate]] runs to ensure your project can
* actually be generated with g8 and then run the
Expand All @@ -59,16 +109,26 @@ trait G8Module extends TaskModule {
val targets = validationTargets()
val log = T.log

val exeSuffix = if (scala.util.Properties.isWin) ".bat" else ""

val mill = if (os.exists(projectPath / s"mill${exeSuffix}")) {
s"./mill${exeSuffix}"
} else if (os.exists(projectPath / s"millw${exeSuffix}")) {
s"./millw${exeSuffix}"
} else {
downloadMill().path.toString()
}

val d = downloadMill()
log.info(d.toString())
val results: Seq[(String, Int)] = targets.zipWithIndex.map {
case (command, id) =>
log.info(
s"""[${id + 1}/${targets.size}] attempting to run "${command}""""
)
// TODO right now we assume this is here, but we should download it if it's not
// TODO we assume non-windows -- we'll need to account for .bat
// TODO do we want --no-server to be configurable?
val cmd = os
.proc("./mill", "--no-server", command)
.proc(mill, "--no-server", command)
.call(cwd = projectPath)

(command, cmd.exitCode)
Expand All @@ -78,7 +138,7 @@ trait G8Module extends TaskModule {
true
}

// TODO this assumes we want everything to suceed. Add in the ability to fail
// TODO right now we assume that we want everythign to succeed. Should we?
if (failed.isEmpty) {
val msg = "All targets ran successfully!"
log.info(msg)
Expand Down

0 comments on commit baf91b8

Please sign in to comment.