Skip to content
Open
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
166 changes: 94 additions & 72 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,62 +15,51 @@ on:
tags: [v*]

env:
PGP_PASSPHRASE: ${{ secrets.PGP_PASSPHRASE }}
SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }}
SONATYPE_CREDENTIAL_HOST: ${{ secrets.SONATYPE_CREDENTIAL_HOST }}
SONATYPE_USERNAME: ${{ secrets.SONATYPE_USERNAME }}
PGP_SECRET: ${{ secrets.PGP_SECRET }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}


concurrency:
group: ${{ github.workflow }} @ ${{ github.ref }}
cancel-in-progress: true

jobs:
build:
name: Build and Test
name: Test
strategy:
matrix:
os: [ubuntu-latest]
scala: [3.2.2]
os: [ubuntu-22.04]
scala: [3]
java: [temurin@17]
project: [rootJS, rootJVM]
runs-on: ${{ matrix.os }}
timeout-minutes: 60
steps:
- name: Checkout current branch (full)
uses: actions/checkout@v3
uses: actions/checkout@v5
with:
fetch-depth: 0

- name: Download Java (temurin@17)
id: download-java-temurin-17
if: matrix.java == 'temurin@17'
uses: typelevel/download-java@v2
with:
distribution: temurin
java-version: 17
- name: Setup sbt
uses: sbt/setup-sbt@v1

- name: Setup Java (temurin@17)
id: setup-java-temurin-17
if: matrix.java == 'temurin@17'
uses: actions/setup-java@v3
uses: actions/setup-java@v5
with:
distribution: jdkfile
distribution: temurin
java-version: 17
jdkFile: ${{ steps.download-java-temurin-17.outputs.jdkFile }}
cache: sbt

- name: Cache sbt
uses: actions/cache@v3
with:
path: |
~/.sbt
~/.ivy2/cache
~/.coursier/cache/v1
~/.cache/coursier/v1
~/AppData/Local/Coursier/Cache/v1
~/Library/Caches/Coursier/v1
key: ${{ runner.os }}-sbt-cache-v2-${{ hashFiles('**/*.sbt') }}-${{ hashFiles('project/build.properties') }}
- name: sbt update
if: matrix.java == 'temurin@17' && steps.setup-java-temurin-17.outputs.cache-hit == 'false'
run: sbt +update

- name: Check that workflows are up to date
run: sbt githubWorkflowCheck

- name: Check headers and formatting
if: matrix.java == 'temurin@17' && matrix.os == 'ubuntu-latest'
if: matrix.java == 'temurin@17' && matrix.os == 'ubuntu-22.04'
run: sbt 'project ${{ matrix.project }}' '++ ${{ matrix.scala }}' headerCheckAll scalafmtCheckAll 'project /' scalafmtSbtCheck

- name: scalaJSLink
Expand All @@ -81,24 +70,24 @@ jobs:
run: sbt 'project ${{ matrix.project }}' '++ ${{ matrix.scala }}' test

- name: Check binary compatibility
if: matrix.java == 'temurin@17' && matrix.os == 'ubuntu-latest'
if: matrix.java == 'temurin@17' && matrix.os == 'ubuntu-22.04'
run: sbt 'project ${{ matrix.project }}' '++ ${{ matrix.scala }}' mimaReportBinaryIssues

- name: Generate API documentation
if: matrix.java == 'temurin@17' && matrix.os == 'ubuntu-latest'
if: matrix.java == 'temurin@17' && matrix.os == 'ubuntu-22.04'
run: sbt 'project ${{ matrix.project }}' '++ ${{ matrix.scala }}' doc

- name: Make target directories
if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main')
run: mkdir -p target .js/target tests/.js/target .jvm/target .native/target maven/.js/target tests/.jvm/target maven/.jvm/target project/target
run: mkdir -p maven/.js/target maven/.jvm/target project/target

- name: Compress target directories
if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main')
run: tar cf targets.tar target .js/target tests/.js/target .jvm/target .native/target maven/.js/target tests/.jvm/target maven/.jvm/target project/target
run: tar cf targets.tar maven/.js/target maven/.jvm/target project/target

- name: Upload target directories
if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main')
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v5
with:
name: target-${{ matrix.os }}-${{ matrix.java }}-${{ matrix.scala }}-${{ matrix.project }}
path: targets.tar
Expand All @@ -109,74 +98,107 @@ jobs:
if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main')
strategy:
matrix:
os: [ubuntu-latest]
scala: [3.2.2]
os: [ubuntu-22.04]
java: [temurin@17]
runs-on: ${{ matrix.os }}
steps:
- name: Checkout current branch (full)
uses: actions/checkout@v3
uses: actions/checkout@v5
with:
fetch-depth: 0

- name: Download Java (temurin@17)
id: download-java-temurin-17
if: matrix.java == 'temurin@17'
uses: typelevel/download-java@v2
with:
distribution: temurin
java-version: 17
- name: Setup sbt
uses: sbt/setup-sbt@v1

- name: Setup Java (temurin@17)
id: setup-java-temurin-17
if: matrix.java == 'temurin@17'
uses: actions/setup-java@v3
uses: actions/setup-java@v5
with:
distribution: jdkfile
distribution: temurin
java-version: 17
jdkFile: ${{ steps.download-java-temurin-17.outputs.jdkFile }}
cache: sbt

- name: Cache sbt
uses: actions/cache@v3
with:
path: |
~/.sbt
~/.ivy2/cache
~/.coursier/cache/v1
~/.cache/coursier/v1
~/AppData/Local/Coursier/Cache/v1
~/Library/Caches/Coursier/v1
key: ${{ runner.os }}-sbt-cache-v2-${{ hashFiles('**/*.sbt') }}-${{ hashFiles('project/build.properties') }}

- name: Download target directories (3.2.2, rootJS)
uses: actions/download-artifact@v3
- name: sbt update
if: matrix.java == 'temurin@17' && steps.setup-java-temurin-17.outputs.cache-hit == 'false'
run: sbt +update

- name: Download target directories (3, rootJS)
uses: actions/download-artifact@v6
with:
name: target-${{ matrix.os }}-${{ matrix.java }}-3.2.2-rootJS
name: target-${{ matrix.os }}-${{ matrix.java }}-3-rootJS

- name: Inflate target directories (3.2.2, rootJS)
- name: Inflate target directories (3, rootJS)
run: |
tar xf targets.tar
rm targets.tar

- name: Download target directories (3.2.2, rootJVM)
uses: actions/download-artifact@v3
- name: Download target directories (3, rootJVM)
uses: actions/download-artifact@v6
with:
name: target-${{ matrix.os }}-${{ matrix.java }}-3.2.2-rootJVM
name: target-${{ matrix.os }}-${{ matrix.java }}-3-rootJVM

- name: Inflate target directories (3.2.2, rootJVM)
- name: Inflate target directories (3, rootJVM)
run: |
tar xf targets.tar
rm targets.tar

- name: Import signing key
if: env.PGP_SECRET != '' && env.PGP_PASSPHRASE == ''
run: echo $PGP_SECRET | base64 -di | gpg --import
env:
PGP_SECRET: ${{ secrets.PGP_SECRET }}
PGP_PASSPHRASE: ${{ secrets.PGP_PASSPHRASE }}
run: echo $PGP_SECRET | base64 -d -i - | gpg --import

- name: Import signing key and strip passphrase
if: env.PGP_SECRET != '' && env.PGP_PASSPHRASE != ''
env:
PGP_SECRET: ${{ secrets.PGP_SECRET }}
PGP_PASSPHRASE: ${{ secrets.PGP_PASSPHRASE }}
run: |
echo "$PGP_SECRET" | base64 -di > /tmp/signing-key.gpg
echo "$PGP_SECRET" | base64 -d -i - > /tmp/signing-key.gpg
echo "$PGP_PASSPHRASE" | gpg --pinentry-mode loopback --passphrase-fd 0 --import /tmp/signing-key.gpg
(echo "$PGP_PASSPHRASE"; echo; echo) | gpg --command-fd 0 --pinentry-mode loopback --change-passphrase $(gpg --list-secret-keys --with-colons 2> /dev/null | grep '^sec:' | cut --delimiter ':' --fields 5 | tail -n 1)

- name: Publish
run: sbt '++ ${{ matrix.scala }}' tlRelease
env:
SONATYPE_USERNAME: ${{ secrets.SONATYPE_USERNAME }}
SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }}
SONATYPE_CREDENTIAL_HOST: ${{ secrets.SONATYPE_CREDENTIAL_HOST }}
run: sbt tlCiRelease

dependency-submission:
name: Submit Dependencies
if: github.event.repository.fork == false && github.event_name != 'pull_request'
strategy:
matrix:
os: [ubuntu-22.04]
java: [temurin@17]
runs-on: ${{ matrix.os }}
steps:
- name: Checkout current branch (full)
uses: actions/checkout@v5
with:
fetch-depth: 0

- name: Setup sbt
uses: sbt/setup-sbt@v1

- name: Setup Java (temurin@17)
id: setup-java-temurin-17
if: matrix.java == 'temurin@17'
uses: actions/setup-java@v5
with:
distribution: temurin
java-version: 17
cache: sbt

- name: sbt update
if: matrix.java == 'temurin@17' && steps.setup-java-temurin-17.outputs.cache-hit == 'false'
run: sbt +update

- name: Submit Dependencies
uses: scalacenter/sbt-dependency-submission@v2
with:
modules-ignore: rootjs_3 tests_sjs1_3 rootjvm_3 rootnative_3 tests_3
configs-ignore: test scala-tool scala-doc-tool test-internal
12 changes: 5 additions & 7 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,16 @@ ThisBuild / organizationName := "Arman Bilge"
ThisBuild / developers += tlGitHubDev("armanbilge", "Arman Bilge")
ThisBuild / startYear := Some(2023)

ThisBuild / tlSonatypeUseLegacyHost := false

ThisBuild / crossScalaVersions := Seq("3.2.2")
ThisBuild / crossScalaVersions := Seq("3.3.7")

ThisBuild / githubWorkflowJavaVersions := Seq(JavaSpec.temurin("17"))
ThisBuild / tlJdkRelease := Some(8)

val CatsEffectVersion = "3.4.7"
val Fs2Version = "3.6.1"
val Http4sVersion = "0.23.18"
val CatsEffectVersion = "3.6.3"
val Fs2Version = "3.12.2"
val Http4sVersion = "0.23.33"
val Fs2DataVersion = "1.6.1"
val Http4sFs2DataVersion = "0.1.0"
val Http4sFs2DataVersion = "0.4.0"

ThisBuild / scalacOptions ++= Seq("-new-syntax", "-indent", "-source:future")

Expand Down
1 change: 0 additions & 1 deletion maven/src/main/scala/huckle/maven/model.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
package huckle.maven

import cats.syntax.all.*
import fs2.data.xml.dom.DocumentBuilder

final case class MavenCoordinates(
groupId: String,
Expand Down
15 changes: 4 additions & 11 deletions maven/src/main/scala/huckle/maven/resolver.scala
Original file line number Diff line number Diff line change
Expand Up @@ -54,22 +54,15 @@ object MavenResolver:
Files[F].exists(path)

def resolve(repos: NonEmptyList[Uri]): F[Option[Uri]] =
val race = repos
repos
.map { repo =>
val uri = toUri(repo) / getFileName("pom")
client
.successful(Request(Method.HEAD, uri))
.ifM(
F.pure(repo),
F.canceled *> F.never,
)
.handleErrorWith(_ => F.canceled *> F.never)
.map(success => if success then Some(repo) else None)
.handleErrorWith(_ => F.pure(None))
}
.reduce(_.race(_).map(_.merge))

race.background.use { oc =>
oc.flatMap(_.fold(F.pure(None), F.raiseError(_), _.map(Some(_))))
}
.reduce(utils.raceOption(_, _))

def downloadMavenProject(repository: Uri): F[Unit] =
val path = cache / toPath
Expand Down
73 changes: 73 additions & 0 deletions maven/src/main/scala/huckle/maven/utils.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/*
* Copyright 2023 Arman Bilge
*
* 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 huckle.maven

import cats.effect.kernel.Concurrent
import cats.effect.kernel.Outcome
import cats.syntax.all.*

object utils:
/** Races two computations that return `Option[A]`. The first `Some` wins and the other
* computation is cancelled. `None` signals "forfeiting" - if one side returns `None`, we
* wait for the other. If both return `None`, the result is `None`.
*/
def raceOption[F[_], A](left: F[Option[A]], right: F[Option[A]])(using
F: Concurrent[F],
): F[Option[A]] =
F.uncancelable { poll =>
poll(F.racePair(left, right)).flatMap {
case Left((oc, f)) =>
oc match
case Outcome.Succeeded(fa) =>
fa.flatMap {
case Some(a) => f.cancel.as(Some(a))
case None =>
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We won the race, but we decided to "forfeit" so we have to look to the loser to decide what to do.

poll(f.join).flatMap {
case Outcome.Succeeded(fb) => fb
case Outcome.Errored(e) => F.raiseError(e)
case Outcome.Canceled() => poll(F.canceled) *> F.never
}
}
case Outcome.Errored(e) => f.cancel *> F.raiseError(e)
case Outcome.Canceled() =>
f.cancel *> f.join.flatMap {
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We copy the new semantics of race here because we are no longer using self-cancelation for anything meaningful, instead we use None to signal "forfeiting".

case Outcome.Succeeded(fb) => fb
case Outcome.Errored(e) => F.raiseError(e)
case Outcome.Canceled() => poll(F.canceled) *> F.never
}

case Right((f, oc)) =>
oc match
case Outcome.Succeeded(fb) =>
fb.flatMap {
case Some(b) => f.cancel.as(Some(b))
case None =>
poll(f.join).flatMap {
case Outcome.Succeeded(fa) => fa
case Outcome.Errored(e) => F.raiseError(e)
case Outcome.Canceled() => poll(F.canceled) *> F.never
}
}
case Outcome.Errored(e) => f.cancel *> F.raiseError(e)
case Outcome.Canceled() =>
f.cancel *> f.join.flatMap {
case Outcome.Succeeded(fa) => fa
case Outcome.Errored(e) => F.raiseError(e)
case Outcome.Canceled() => poll(F.canceled) *> F.never
}
}
}
2 changes: 1 addition & 1 deletion project/build.properties
Original file line number Diff line number Diff line change
@@ -1 +1 @@
sbt.version=1.8.2
sbt.version=1.11.7
Loading