Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support windows and projects with no mill #3

Merged
merged 1 commit into from
Aug 15, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 15 additions & 3 deletions .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 @@ -46,13 +47,24 @@ jobs:
distribution: 'temurin'
java-version: ${{ matrix.java }}

- name: Compile
- name: Compile Ubuntu
run:
./mill -i __.compile
if: matrix.os == 'ubuntu-latest'

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

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

- name: Test Windows
run:
./millw.bat -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.
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