Skip to content

Commit 869365f

Browse files
committed
Fix implementation
1 parent dd48bfb commit 869365f

File tree

2 files changed

+114
-140
lines changed

2 files changed

+114
-140
lines changed

.github/workflows/buildBisect.yaml

Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ on:
1414
project-targets:
1515
type: string
1616
description: "List of project targets to builds (comma deliomited)"
17-
default: "*"
17+
default: ""
1818
repository-url:
1919
type: string
2020
description: "GitHub repository URL for compiler to build"
@@ -53,24 +53,19 @@ jobs:
5353
repository: ${{ inputs.repository-url }}
5454
ref: main
5555
path: "compiler"
56-
- uses: coursier/cache-action@v6.4
5756

58-
- name: Check java version
59-
shell: bash
60-
run: |
61-
ConfigFile=".github/workflows/buildConfig.json"
62-
DefaultJDK=11
63-
javaVersion=$(jq -r ".\"${{ inputs.project-name }}\".config.java.version // ${DefaultJDK}" $ConfigFile)
64-
echo "java-version=$javaVersion" >> $GITHUB_ENV
65-
echo "JavaVersion set to $javaVersion"
57+
- uses: coursier/cache-action@v6.4
6658

6759
- name: "Bisect project"
6860
shell: bash
6961
run: |
62+
cd ${{ github.workspace }}/compiler
63+
git --no-pager log -1
7064
${{ github.workspace }}/opencb/scripts/bisect.scala -- \
71-
--releases ${{ inputs.scala-version-start}}..${{ inputs.scala-version-end}} \
72-
--extra-scalac-options ${{ inputs.extra-scalac-options }} \
73-
--disabled-scalac-options ${{ inputs.disabled-scalac-options }} \
74-
--community-build-dir ${{ github.workspace }}/opencb \
75-
--compiler-dir ${{ github.workspace }}/compiler \
76-
${{ inputs.project-name }} ${{ inputs.project-targets }}
65+
--project-name=${{ inputs.project-name }} \
66+
--targets=${{ inputs.project-targets }} \
67+
--releases=${{ inputs.scala-version-start}}..${{ inputs.scala-version-end}} \
68+
--extra-scalac-options=${{ inputs.extra-scalac-options }} \
69+
--disabled-scalac-options=${{ inputs.disabled-scalac-options }} \
70+
--community-build-dir=${{ github.workspace }}/opencb \
71+
--compiler-dir=${{ github.workspace }}/compiler

scripts/bisect.scala

Lines changed: 103 additions & 124 deletions
Original file line numberDiff line numberDiff line change
@@ -1,127 +1,121 @@
11
// Based on https://github.com/lampepfl/dotty/blob/main/project/scripts/bisect.scala
2-
3-
/*
4-
This script will bisect a problem with the compiler based on success/failure of the validation script passed as an argument.
5-
It starts with a fast bisection on released nightly builds.
6-
Then it will bisect the commits between the last nightly that worked and the first nightly that failed.
7-
Look at the `usageMessage` below for more details.
8-
*/
9-
10-
11-
2+
//> using lib "com.github.scopt::scopt:4.1.0"
3+
//> using scala 3.3
124

135
import sys.process._
146
import scala.io.Source
157
import java.io.File
168
import java.nio.file.attribute.PosixFilePermissions
179
import java.nio.charset.StandardCharsets
18-
import java.nio.file.Files
19-
20-
// --extra-scalac-options ${{ inputs.extra-scalac-options }} \
21-
// --disabled-scalac-options ${{ inputs.disabled-scalac-options }} \
22-
// --community-build-dir ${{ github.workspace }}/opencb
10+
import java.nio.file._
2311

24-
val usageMessage = """
25-
|Usage:
26-
| > scala-cli project/scripts/bisect.scala -- [<bisect-options>] <projectName> <targets>*
27-
|
28-
|The optional <bisect-options> may be any combination of:
29-
|* --dry-run
30-
| Don't try to bisect - just make sure the validation command works correctly
31-
|
32-
|* --extra-scalac-options <options>
33-
| Comma delimited of additional scalacOptions passed to project build
34-
|
35-
|* --disabled-scalac-options <options>
36-
| Comma delimited of disabled scalacOptions passed to project build
37-
|
38-
|* --community-build-dir <path>
39-
| Directory with community build project from which the project config would be resolved
40-
|
41-
|* --compiler-dir <path>
42-
| Directory containing Scala compiler repository, required for commit-based bissect
43-
|
44-
|* --releases <releases-range>
45-
| Bisect only releases from the given range (defaults to all releases).
46-
| The range format is <first>..<last>, where both <first> and <last> are optional, e.g.
47-
| * 3.1.0-RC1-bin-20210827-427d313-NIGHTLY..3.2.1-RC1-bin-20220716-bb9c8ff-NIGHTLY
48-
| * 3.2.1-RC1-bin-20220620-de3a82c-NIGHTLY..
49-
| * ..3.3.0-RC1-bin-20221124-e25362d-NIGHTLY
50-
| The ranges are treated as inclusive.
51-
|
52-
|* --should-fail
53-
| Expect the validation command to fail rather that succeed. This can be used e.g. to find out when some illegal code started to compile.
54-
|
55-
|Warning: The bisect script should not be run multiple times in parallel because of a potential race condition while publishing artifacts locally.
56-
57-
""".stripMargin
12+
val communityBuildVersion = "v0.2.4"
5813

5914
@main def run(args: String*): Unit =
60-
val scriptOptions =
61-
try ScriptOptions.fromArgs(args)
62-
catch
63-
case _ =>
64-
sys.error(s"Wrong script parameters.\n${usageMessage}")
65-
66-
val validationScript = scriptOptions.validationCommand.validationScript
67-
val releases = Releases.fromRange(scriptOptions.releasesRange)
68-
val releaseBisect = ReleaseBisect(validationScript, shouldFail = scriptOptions.shouldFail, releases)
15+
val config = scopt.OParser
16+
.parse(Config.parser, args, Config())
17+
.getOrElse(sys.error("Failed to parse config"))
18+
19+
val validationScript = config.validationScript
20+
val releases = Releases.fromRange(config.releasesRange)
21+
val releaseBisect = ReleaseBisect(validationScript, shouldFail = config.shouldFail, releases)
6922

7023
releaseBisect.verifyEdgeReleases()
7124

72-
if (!scriptOptions.dryRun) then
25+
if (!config.dryRun) then
7326
val (lastGoodRelease, firstBadRelease) = releaseBisect.bisectedGoodAndBadReleases()
7427
println(s"Last good release: ${lastGoodRelease.version}")
7528
println(s"First bad release: ${firstBadRelease.version}")
7629
println("\nFinished bisecting releases\n")
7730

78-
val commitBisect = CommitBisect(validationScript, shouldFail = scriptOptions.shouldFail, lastGoodRelease.hash, firstBadRelease.hash)
31+
val commitBisect = CommitBisect(validationScript, shouldFail = config.shouldFail, lastGoodRelease.hash, firstBadRelease.hash)
7932
commitBisect.bisect()
8033

81-
82-
case class ScriptOptions(validationCommand: ValidationCommand, dryRun: Boolean, releasesRange: ReleasesRange, shouldFail: Boolean)
83-
object ScriptOptions:
84-
def fromArgs(args: Seq[String]) =
85-
val defaultOptions = ScriptOptions(
86-
validationCommand = null,
87-
dryRun = false,
88-
ReleasesRange(first = None, last = None),
89-
shouldFail = false
90-
)
91-
parseArgs(args, defaultOptions)
92-
93-
private def parseArgs(args: Seq[String], options: ScriptOptions): ScriptOptions =
94-
println(s"parse: $args")
95-
args match
96-
case "--dry-run" :: argsRest => parseArgs(argsRest, options.copy(dryRun = true))
97-
case "--releases" :: argsRest =>
98-
val range = ReleasesRange.tryParse(argsRest.head).get
99-
parseArgs(argsRest.tail, options.copy(releasesRange = range))
100-
case "--should-fail" :: argsRest => parseArgs(argsRest, options.copy(shouldFail = true))
101-
case args =>
102-
val command = ValidationCommand.fromArgs(args)
103-
options.copy(validationCommand = command)
104-
105-
case class ValidationCommand(projectName: String, openCommunityBuildDir: File, targets: Seq[String]):
106-
val remoteValidationScript: File = ValidationScript.buildProject(
107-
projectName = projectName,
108-
targets = Option.when(targets.nonEmpty)(targets.mkString(" ")),
109-
extraScalacOptions = "",
110-
disabledScalacOption="",
111-
runId ="test",
112-
buildURL="",
113-
executeTests = false
34+
case class ValidationCommand(projectName: String = "", targets: String = "", extraScalacOptions: String = "", disabledScalacOption: String = "")
35+
case class Config(
36+
dryRun: Boolean = false,
37+
releasesRange: ReleasesRange = ReleasesRange.all,
38+
shouldFail: Boolean = false,
39+
openCommunityBuildDir: Path = Path.of(""),
40+
compilerDir: Path = Path.of(""),
41+
command: ValidationCommand = ValidationCommand()
42+
){
43+
inline def withCommand(mapping: ValidationCommand => ValidationCommand) = copy(command = mapping(command))
44+
45+
lazy val remoteValidationScript: File = ValidationScript.buildProject(
46+
projectName = command.projectName,
47+
targets = Option(command.targets).filter(_.nonEmpty),
48+
extraScalacOptions = command.extraScalacOptions,
49+
disabledScalacOption= command.disabledScalacOption,
50+
runId = s"bisect-${command.projectName}",
51+
buildURL= "",
52+
executeTests = false,
53+
openCBDir = openCommunityBuildDir
11454
)
115-
val validationScript: File = ValidationScript.dockerRunBuildProject(projectName, remoteValidationScript, openCommunityBuildDir)
116-
117-
object ValidationCommand:
118-
def fromArgs(args: Seq[String]) =
119-
args match
120-
case Seq(projectName, openCBDir, targets*) => ValidationCommand(projectName, new File(openCBDir), targets)
121-
55+
lazy val validationScript: File =
56+
require(Files.exists(openCommunityBuildDir), "Open CB dir does not exist")
57+
require(Files.exists(compilerDir), "Compiler dir does not exist")
58+
ValidationScript.dockerRunBuildProject(command.projectName, remoteValidationScript, openCommunityBuildDir.toFile())
59+
}
60+
61+
object Config{
62+
val parser = {
63+
import scopt.OParser
64+
val builder = OParser.builder[Config]
65+
import builder.*
66+
OParser.sequence(
67+
head("Scala 3 Open Community Build bisect", communityBuildVersion),
68+
opt[Unit]("dry-run")
69+
.action:
70+
(_, c) => c.copy(dryRun = true)
71+
.text("Don't try to bisect - just make sure the validation command works correctly"),
72+
opt[String]("releases")
73+
.action:
74+
(v, c) => c.copy(releasesRange = ReleasesRange.tryParse(v).getOrElse(c.releasesRange))
75+
.text("Bisect only releases from the given range 'first..last' (defaults to all releases)"),
76+
opt[Unit]("should-fail")
77+
.action:
78+
(_, c) => c.copy(shouldFail = true)
79+
.text("Expect the validation command to fail rather that succeed. This can be used e.g. to find out when some illegal code started to compile"),
80+
opt[String]("project-name")
81+
.action:
82+
(v, c) => c.withCommand(_.copy(projectName =v ))
83+
.text("Name of the project to run using GitHub coordinates")
84+
.required(),
85+
opt[String]("targets")
86+
.action:
87+
(v, c) => c.withCommand(_.copy(targets = v))
88+
.text("Comma delimited list of targets to limit scope of project building"),
89+
opt[String]("extra-scalac-options")
90+
.action:
91+
(v, c) => c.withCommand(_.copy(extraScalacOptions = v))
92+
.text("Extra scalac options passed to project build"),
93+
opt[String]("disabled-scalac-options")
94+
.action:
95+
(v, c) => c.withCommand(_.copy(disabledScalacOption = v))
96+
.text("Filtered out scalac options passed to project build"),
97+
opt[String]("community-build-dir")
98+
.action:
99+
(v, c) => c.copy(openCommunityBuildDir = Path.of(v))
100+
.text("Directory with community build project from which the project config would be resolved")
101+
.required(),
102+
opt[String]("compiler-dir")
103+
.action:
104+
(v, c) => c.copy(compilerDir = Path.of(v))
105+
.text("Directory containing Scala compiler repository, required for commit-based bissect")
106+
.required()
107+
,
108+
checkConfig { c =>
109+
if !Files.exists(c.compilerDir) then failure("Compiler directory does not exist")
110+
else if !Files.exists(c.openCommunityBuildDir) then failure("Open Community Build directory does not exist")
111+
else success
112+
}
113+
)
114+
}
115+
}
122116

123117
object ValidationScript:
124-
def buildProject(projectName: String, targets: Option[String], extraScalacOptions: String, disabledScalacOption: String, runId: String, buildURL: String, executeTests: Boolean): File = tmpScript{
118+
def buildProject(projectName: String, targets: Option[String], extraScalacOptions: String, disabledScalacOption: String, runId: String, buildURL: String, executeTests: Boolean, openCBDir: Path): File = tmpScript(openCBDir){
125119
val configPatch =
126120
if executeTests
127121
then ""
@@ -159,31 +153,16 @@ object ValidationScript:
159153
| "$extraScalacOptions" \
160154
| "$disabledScalacOption"
161155
|
162-
|#/build/feed-elastic.sh \
163-
|# 'https://scala3.westeurope.cloudapp.azure.com/data' \
164-
|# "${projectName}" \
165-
|# "$$(cat build-status.txt)" \
166-
|# "$$(date --iso-8601=seconds)" \
167-
|# build-summary.txt \
168-
|# build-logs.txt \
169-
|# "$$(config .version)" \
170-
|# "$${scalaVersion}" \
171-
|# "${runId}" \
172-
|# "${buildURL}"
173-
|#
174-
|#if [ $$? != 0 ]; then
175-
|# echo "::warning title=Indexing failure::Indexing results of ${projectName} failed"
176-
|#fi
177-
|
178156
|grep -q "success" build-status.txt;
179157
|exit $$?
180158
""".stripMargin
181159
}
182160

183161
def dockerRunBuildProject(projectName: String, validationScript: File, openCBDir: File): File =
162+
val scriptsPath = "/scripts/"
184163
val validationScriptPath="/scripts/validationScript.sh"
185-
val imageVersion = "v0.2.4"
186-
tmpScript(raw"""
164+
assert(Files.exists(validationScript.toPath()))
165+
tmpScript(openCBDir.toPath)(raw"""
187166
|#!/usr/bin/env bash
188167
|set -e
189168
|scalaVersion=$$1
@@ -193,13 +172,13 @@ object ValidationScript:
193172
|docker run --rm \
194173
| -v ${validationScript.getAbsolutePath()}:$validationScriptPath \
195174
| -v ${openCBDir.getAbsolutePath()}:/opencb/ \
196-
| virtuslab/scala-community-build-project-builder:jdk$${javaVersion}-$imageVersion \
197-
| /bin/bash $validationScriptPath $$scalaVersion
175+
| virtuslab/scala-community-build-project-builder:jdk$${javaVersion}-$communityBuildVersion \
176+
| /bin/bash -c "$validationScriptPath $$scalaVersion"
198177
""".stripMargin)
199-
200-
private def tmpScript(content: String): File =
178+
179+
private def tmpScript(openCBDir: Path)(content: String): File =
201180
val executableAttr = PosixFilePermissions.asFileAttribute(PosixFilePermissions.fromString("rwxr-xr-x"))
202-
val tmpPath = Files.createTempFile("scala-bisect-validator", "", executableAttr)
181+
val tmpPath = Files.createTempFile(openCBDir, "scala-bisect-validator", ".sh", executableAttr)
203182
val tmpFile = tmpPath.toFile
204183

205184
print(s"Bisecting with validation script: ${tmpPath.toAbsolutePath}\n")

0 commit comments

Comments
 (0)