From 63a254f28c1a58eb0582126fc68814166e88cd04 Mon Sep 17 00:00:00 2001 From: Luca Tassinari Date: Tue, 20 Feb 2024 00:24:28 +0100 Subject: [PATCH] ci: add build and deployment of documentation website --- .github/workflows/build-test-check.yaml | 5 ++ .github/workflows/publish-doc.yaml | 71 +++++++++++-------- README.md | 11 --- .../scala/io/github/tassiLuca/Launcher.scala | 24 ------- .../github/tassiLuca/boundary/Boundary.scala | 11 --- .../tassiLuca/boundary/BoundarySource.scala | 18 ----- .../tassiLuca/boundary/impl/SwingUI.scala | 50 ------------- .../tassiLuca/boundary/impl/Timer.scala | 16 ----- .../io/github/tassiLuca/core/Controller.scala | 44 ------------ .../io/github/tassiLuca/core/Event.scala | 6 -- .../io/github/tassiLuca/core/Reaction.scala | 9 --- .../io/github/tassiLuca/core/Space.scala | 53 -------------- .../io/github/tassiLuca/utils/Types2D.scala | 17 ----- .../tassiLuca/boundary/impl/TimerTest.scala | 33 --------- .../io/github/tassiLuca/core/SpaceTest.scala | 35 --------- docs/content/_index.md | 2 +- docs/go.mod | 2 +- settings.gradle.kts | 1 - 18 files changed, 47 insertions(+), 361 deletions(-) delete mode 100644 README.md delete mode 100644 back-and-forth/src/main/scala/io/github/tassiLuca/Launcher.scala delete mode 100644 back-and-forth/src/main/scala/io/github/tassiLuca/boundary/Boundary.scala delete mode 100644 back-and-forth/src/main/scala/io/github/tassiLuca/boundary/BoundarySource.scala delete mode 100644 back-and-forth/src/main/scala/io/github/tassiLuca/boundary/impl/SwingUI.scala delete mode 100644 back-and-forth/src/main/scala/io/github/tassiLuca/boundary/impl/Timer.scala delete mode 100644 back-and-forth/src/main/scala/io/github/tassiLuca/core/Controller.scala delete mode 100644 back-and-forth/src/main/scala/io/github/tassiLuca/core/Event.scala delete mode 100644 back-and-forth/src/main/scala/io/github/tassiLuca/core/Reaction.scala delete mode 100644 back-and-forth/src/main/scala/io/github/tassiLuca/core/Space.scala delete mode 100644 back-and-forth/src/main/scala/io/github/tassiLuca/utils/Types2D.scala delete mode 100644 back-and-forth/src/test/scala/io/github/tassiLuca/boundary/impl/TimerTest.scala delete mode 100644 back-and-forth/src/test/scala/io/github/tassiLuca/core/SpaceTest.scala diff --git a/.github/workflows/build-test-check.yaml b/.github/workflows/build-test-check.yaml index 9c95d200..aab94a5e 100644 --- a/.github/workflows/build-test-check.yaml +++ b/.github/workflows/build-test-check.yaml @@ -10,6 +10,7 @@ on: - 'LICENSE' - 'README.md' - 'renovate.json' + - 'docs/**' pull_request: workflow_dispatch: @@ -38,8 +39,12 @@ jobs: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Check run: ./gradlew check + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Build run: ./gradlew classes testClasses + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} success: needs: build diff --git a/.github/workflows/publish-doc.yaml b/.github/workflows/publish-doc.yaml index f08c7947..5e24027d 100644 --- a/.github/workflows/publish-doc.yaml +++ b/.github/workflows/publish-doc.yaml @@ -1,46 +1,55 @@ -name: Publish on GH Pages the documentation - +name: Build and deploy on: push: paths: - - "docs/**" + - 'docs/**' + - '.github/workflows/publish-doc.yaml' + pull_request: workflow_dispatch: # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages permissions: - contents: read + contents: write pages: write id-token: write -# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. -# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. -concurrency: - group: "pages" - cancel-in-progress: false - jobs: - build: + Build-Documentation-Site: runs-on: ubuntu-latest + concurrency: + group: slides-${{ github.ref }} + cancel-in-progress: false steps: - name: Checkout - uses: actions/checkout@v4 - - name: Setup Pages - uses: actions/configure-pages@v4 - - name: Build with Jekyll - uses: actions/jekyll-build-pages@v1 + uses: danysk/action-checkout@0.2.14 + - name: Compute the version of Hugo + id: hugo + shell: bash + run: | + USES=$(cat <> "$GITHUB_OUTPUT" + - name: Download Hugo + run: | + HUGO_VERSION="${{ steps.hugo.outputs.version }}" + URL="https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_extended_${HUGO_VERSION}_linux-amd64.deb" + wget --retry-connrefused --waitretry=1 --read-timeout=20 "$URL" --output-document=hugo.deb + - name: Install Hugo + run: sudo dpkg -i hugo.deb + - name: Remove Hugo Installer + run: rm hugo.deb + - name: Build slides with hugo + run: | + cd docs + hugo + - name: Deployment + if: github.ref == 'refs/heads/master' + uses: peaceiris/actions-gh-pages@v3 with: - source: docs - destination: ./_site - - name: Upload artifact - uses: actions/upload-pages-artifact@v3 - - publish: - environment: - name: github-pages - url: ${{ steps.deployment.outputs.page_url }} - runs-on: ubuntu-latest - needs: build - steps: - - name: Deploy to GitHub Pages - id: deployment - uses: actions/deploy-pages@v4 + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_branch: docs + publish_dir: ./docs/public diff --git a/README.md b/README.md deleted file mode 100644 index a46233dd..00000000 --- a/README.md +++ /dev/null @@ -1,11 +0,0 @@ -# Project for PPS course @ UniBo - -## Documentation - -The documentation is available [here](https://tassiluca.github.io/PPS-22-direct-style-experiments/). - -## Installation - -```bash -git clone --recurse-submodules https://github.com/tassiLuca/PPS-22-direct-style-experiments.git direct-style-experiments -``` diff --git a/back-and-forth/src/main/scala/io/github/tassiLuca/Launcher.scala b/back-and-forth/src/main/scala/io/github/tassiLuca/Launcher.scala deleted file mode 100644 index 42db06bc..00000000 --- a/back-and-forth/src/main/scala/io/github/tassiLuca/Launcher.scala +++ /dev/null @@ -1,24 +0,0 @@ -package io.github.tassiLuca - -import gears.async.default.given -import gears.async.Async - -import concurrent.duration.DurationInt -import io.github.tassiLuca.boundary.impl.{SwingUI, Timer} -import io.github.tassiLuca.core.{Controller, Space2D} - -import scala.language.postfixOps - -@main def main(): Unit = Async.blocking: - val view = SwingUI(500, 800) - val timer = Timer(1 seconds) - view.asRunnable.run - timer.asRunnable.run - val controller = Controller - .reactive( - boundaries = Set(timer), - updatableBoundaries = Set(view), - reaction = (_, s) => s, - )(Space2D((450, 700))) - .run - controller.await diff --git a/back-and-forth/src/main/scala/io/github/tassiLuca/boundary/Boundary.scala b/back-and-forth/src/main/scala/io/github/tassiLuca/boundary/Boundary.scala deleted file mode 100644 index 62f5e62e..00000000 --- a/back-and-forth/src/main/scala/io/github/tassiLuca/boundary/Boundary.scala +++ /dev/null @@ -1,11 +0,0 @@ -package io.github.tassiLuca.boundary - -import gears.async.{Async, Task} -import io.github.tassiLuca.core.Event - -trait Boundary: - def src: Async.Source[Event] - def asRunnable: Task[Unit] - -trait UpdatableBoundary[M] extends Boundary: - def update(model: M): Unit diff --git a/back-and-forth/src/main/scala/io/github/tassiLuca/boundary/BoundarySource.scala b/back-and-forth/src/main/scala/io/github/tassiLuca/boundary/BoundarySource.scala deleted file mode 100644 index db5cc6fa..00000000 --- a/back-and-forth/src/main/scala/io/github/tassiLuca/boundary/BoundarySource.scala +++ /dev/null @@ -1,18 +0,0 @@ -package io.github.tassiLuca.boundary - -import gears.async.{Async, Listener} -import io.github.tassiLuca.core.Event - -object BoundarySource extends Async.OriginalSource[Event]: - private[boundary] var listeners = Set[Listener[Event]]() - - override def poll(k: Listener[Event]): Boolean = false - - override def dropListener(k: Listener[Event]): Unit = synchronized: - listeners = listeners - k - - override protected def addListener(k: Listener[Event]): Unit = synchronized: - listeners = listeners + k - - def notifyListeners(e: Event): Unit = synchronized: - listeners.foreach(_.completeNow(e, this)) diff --git a/back-and-forth/src/main/scala/io/github/tassiLuca/boundary/impl/SwingUI.scala b/back-and-forth/src/main/scala/io/github/tassiLuca/boundary/impl/SwingUI.scala deleted file mode 100644 index e6dd8a83..00000000 --- a/back-and-forth/src/main/scala/io/github/tassiLuca/boundary/impl/SwingUI.scala +++ /dev/null @@ -1,50 +0,0 @@ -package io.github.tassiLuca.boundary.impl - -import gears.async.{Async, Task} -import io.github.tassiLuca.boundary.{BoundarySource, UpdatableBoundary} -import io.github.tassiLuca.core.Event.{ChangeDirection, Freeze} -import io.github.tassiLuca.core.{Event, RectangularEntities, Space2D} - -import java.awt.event.{KeyAdapter, KeyEvent} -import java.awt.{BorderLayout, Color, Graphics} -import javax.swing.border.LineBorder -import javax.swing.{JFrame, JPanel, SwingUtilities, WindowConstants} - -class SwingUI(width: Int, height: Int) extends UpdatableBoundary[Space2D & RectangularEntities]: - private val boundarySource = BoundarySource - private val frame = JFrame("Popping Bubbles game") - - frame.addKeyListener( - new KeyAdapter: - override def keyPressed(e: KeyEvent): Unit = e.getKeyCode match - case 38 | 40 => boundarySource.notifyListeners(ChangeDirection) - case 32 => boundarySource.notifyListeners(Freeze) - case _ => (), - ) - - override inline final def src: Async.Source[Event] = boundarySource - - override def asRunnable: Task[Unit] = Task { - frame.setSize(width, height) - frame.setVisible(true) - frame.setLocationRelativeTo(null) - frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE) - } - - override def update(space: Space2D & RectangularEntities): Unit = SwingUtilities.invokeAndWait { () => - if frame.getContentPane.getComponentCount != 0 then frame.getContentPane.remove(0) - frame.getContentPane.add(WorldPane(space, space.world.shape.width, space.world.shape.height), BorderLayout.CENTER) - frame.getContentPane.repaint() - } - - private class WorldPane(space: Space2D & RectangularEntities, width: Int, height: Int) extends JPanel: - setSize(width, height) - setBorder(LineBorder(Color.DARK_GRAY, 2)) - - override def paintComponent(g: Graphics): Unit = - val (px, py) = space.world.player.position - g.fillRect(px, py, space.world.player.shape.width, space.world.player.shape.height) - space.world.obstacles.foreach(o => - val (px, py) = o.position - g.drawRect(px, py, o.shape.width, o.shape.height), - ) diff --git a/back-and-forth/src/main/scala/io/github/tassiLuca/boundary/impl/Timer.scala b/back-and-forth/src/main/scala/io/github/tassiLuca/boundary/impl/Timer.scala deleted file mode 100644 index 75291980..00000000 --- a/back-and-forth/src/main/scala/io/github/tassiLuca/boundary/impl/Timer.scala +++ /dev/null @@ -1,16 +0,0 @@ -package io.github.tassiLuca.boundary.impl - -import gears.async.{Async, Task} -import gears.async.default.given -import io.github.tassiLuca.boundary.Boundary -import io.github.tassiLuca.core.Event - -import scala.concurrent.duration.Duration - -class Timer(period: Duration) extends Boundary: - private val timer = gears.async.Timer(period) - - override def src: Async.Source[Event] = - timer.src.transformValuesWith(_ => Event.Tick) - - override def asRunnable: Task[Unit] = Task(timer.run()) diff --git a/back-and-forth/src/main/scala/io/github/tassiLuca/core/Controller.scala b/back-and-forth/src/main/scala/io/github/tassiLuca/core/Controller.scala deleted file mode 100644 index 9534859a..00000000 --- a/back-and-forth/src/main/scala/io/github/tassiLuca/core/Controller.scala +++ /dev/null @@ -1,44 +0,0 @@ -package io.github.tassiLuca.core - -import gears.async.TaskSchedule.RepeatUntilFailure -import gears.async.{Async, Future, ReadableChannel, SendableChannel, Task, UnboundedChannel} -import io.github.tassiLuca.boundary.{Boundary, UpdatableBoundary} - -import java.time.LocalTime -import scala.annotation.tailrec - -trait Controller[Event, Model]: - def reactive( - boundaries: Set[Boundary], - updatableBoundaries: Set[UpdatableBoundary[Model]], - reaction: Reaction[Event, Model], - )(initial: Space2D & RectangularEntities): Task[Unit] - -object Controller extends Controller[Event, Space2D & RectangularEntities]: - override def reactive( - boundaries: Set[Boundary], - updatableBoundaries: Set[UpdatableBoundary[Space2D & RectangularEntities]], - reaction: Reaction[Event, Space2D & RectangularEntities], - )(initial: Space2D & RectangularEntities): Task[Unit] = Task: - val channel = UnboundedChannel[Event]() // may go out of memory! - val producers = (boundaries ++ updatableBoundaries).map(_ produceOn channel) - Future { consumeFrom(updatableBoundaries, channel, initial, reaction) } - producers.map(_.run).toSeq.awaitAll // important! - - extension (b: Boundary) - private def produceOn(channel: SendableChannel[Event]): Task[Unit] = Task { - channel.send(b.src.awaitResult) - }.schedule(RepeatUntilFailure()) - - @tailrec - private def consumeFrom( - updatableBoundaries: Set[UpdatableBoundary[Space2D & RectangularEntities]], - channel: ReadableChannel[Event], - space2D: Space2D & RectangularEntities, - reaction: Reaction[Event, Space2D & RectangularEntities], - )(using Async): Unit = - val event = channel.read().toOption.get // may fail due to closed channel - println(s"Consuming $event @ ${LocalTime.now()}") - val newSpace = reaction(event, space2D) - updatableBoundaries.foreach(_.update(newSpace)) - consumeFrom(updatableBoundaries, channel, space2D, reaction) diff --git a/back-and-forth/src/main/scala/io/github/tassiLuca/core/Event.scala b/back-and-forth/src/main/scala/io/github/tassiLuca/core/Event.scala deleted file mode 100644 index 53781591..00000000 --- a/back-and-forth/src/main/scala/io/github/tassiLuca/core/Event.scala +++ /dev/null @@ -1,6 +0,0 @@ -package io.github.tassiLuca.core - -enum Event: - case Tick - case ChangeDirection - case Freeze diff --git a/back-and-forth/src/main/scala/io/github/tassiLuca/core/Reaction.scala b/back-and-forth/src/main/scala/io/github/tassiLuca/core/Reaction.scala deleted file mode 100644 index 91b6172e..00000000 --- a/back-and-forth/src/main/scala/io/github/tassiLuca/core/Reaction.scala +++ /dev/null @@ -1,9 +0,0 @@ -package io.github.tassiLuca.core - -import scala.annotation.targetName - -type Reaction[Event, Model] = (Event, Model) => Model - -extension [A, B, C](f: (A, B) => C) - @targetName("chainWith") - def >>[D](g: (A, C) => D): (A, B) => D = (a, b) => g(a, f(a, b)) diff --git a/back-and-forth/src/main/scala/io/github/tassiLuca/core/Space.scala b/back-and-forth/src/main/scala/io/github/tassiLuca/core/Space.scala deleted file mode 100644 index 543a56dc..00000000 --- a/back-and-forth/src/main/scala/io/github/tassiLuca/core/Space.scala +++ /dev/null @@ -1,53 +0,0 @@ -package io.github.tassiLuca.core - -import io.github.tassiLuca.utils.* - -/** The N-d space on which the game world is absorbed. */ -trait Space: - - type Position - type Speed - type Shape - - def world: World - def world_=(world: World): Unit - - sealed trait Entity: - val position: Position - val speed: Speed - val shape: Shape - - class Player(override val position: Position, override val speed: Speed, override val shape: Shape) extends Entity - class Obstacle(override val position: Position, override val speed: Speed, override val shape: Shape) extends Entity - - case class World(player: Player, obstacles: Set[Obstacle], shape: Shape) - - extension (e: Entity) def isCollidingWith(e2: Entity): Boolean - -/** A 2 dimensional space. */ -trait Space2D extends Space: - override type Speed = Vector2D - override type Position = Point2D - -/** A mixin specifying rectangular shapes along with the definition of collision among them. */ -trait RectangularEntities: - self: Space2D => - - case class RectangularShape(width: Int, height: Int) - - override type Shape = RectangularShape - - extension (e: Entity) - override def isCollidingWith(e2: Entity): Boolean = - (e.position._1 <= e2.position._1 + e2.shape.width) && (e.position._1 + e.shape.width >= e2.position._1) && - (e.position._2 <= e2.position._2 + e2.shape.height) && (e.position._2 + e.shape.height >= e2.position._2) - -object Space2D: - def apply(size: Point2D): Space2D with RectangularEntities = new Space2D with RectangularEntities: - var _world: World = World( - player = Player(size / 2, (0, 1.0), RectangularShape(size._1 / 10, size._2 / 10)), - obstacles = Set.empty, - shape = RectangularShape(size._1, size._2), - ) - override def world: World = _world - override def world_=(world: World): Unit = _world = world diff --git a/back-and-forth/src/main/scala/io/github/tassiLuca/utils/Types2D.scala b/back-and-forth/src/main/scala/io/github/tassiLuca/utils/Types2D.scala deleted file mode 100644 index 24be4cd3..00000000 --- a/back-and-forth/src/main/scala/io/github/tassiLuca/utils/Types2D.scala +++ /dev/null @@ -1,17 +0,0 @@ -package io.github.tassiLuca.utils - -import scala.annotation.targetName - -type Point2D = (Int, Int) - -type Vector2D = (Double, Double) - -extension (p: Point2D) - - @targetName("divideBy") - def /(i: Int): Point2D = (p._1 / i, p._2 / i) - - @targetName("subtract") - def -(i: Int): Point2D = (p._1 - i, p._2 - i) - -extension (v: Vector2D) def module: Double = Math.sqrt(v._1 * v._1 + v._2 * v._2) diff --git a/back-and-forth/src/test/scala/io/github/tassiLuca/boundary/impl/TimerTest.scala b/back-and-forth/src/test/scala/io/github/tassiLuca/boundary/impl/TimerTest.scala deleted file mode 100644 index 19b9e2c6..00000000 --- a/back-and-forth/src/test/scala/io/github/tassiLuca/boundary/impl/TimerTest.scala +++ /dev/null @@ -1,33 +0,0 @@ -package io.github.tassiLuca.boundary.impl - -import gears.async.default.given -import gears.async.{Async, Listener} -import io.github.tassiLuca.core.Event -import org.scalatest.flatspec.AnyFlatSpec -import org.scalatest.matchers.should.Matchers - -import concurrent.duration.DurationInt -import scala.language.postfixOps - -class TimerTest extends AnyFlatSpec with Matchers: - - "Timer when run" should "generate Tick events observable waiting for them" in { - val timer = Timer(1 seconds) - val before = System.currentTimeMillis() - Async.blocking: - timer.asRunnable.run - timer.src.awaitResult shouldBe Event.Tick - val now = System.currentTimeMillis() - (now - before) should (be > 1_000L) - } - - "Timer when run" should "generate Tick events observable attaching a listener" in { - Async.blocking: - val timerPeriod = 1 seconds - val timer = Timer(timerPeriod) - var event: Event = null - timer.src.onComplete(Listener((e, _) => event = e)) - timer.asRunnable.run - Thread.sleep(timerPeriod.toMillis + 100) - event shouldBe Event.Tick - } diff --git a/back-and-forth/src/test/scala/io/github/tassiLuca/core/SpaceTest.scala b/back-and-forth/src/test/scala/io/github/tassiLuca/core/SpaceTest.scala deleted file mode 100644 index fa2da3ae..00000000 --- a/back-and-forth/src/test/scala/io/github/tassiLuca/core/SpaceTest.scala +++ /dev/null @@ -1,35 +0,0 @@ -package io.github.tassiLuca.core - -import org.scalatest.flatspec.AnyFlatSpec -import org.scalatest.matchers.should.Matchers - -class SpaceTest extends AnyFlatSpec with Matchers: - - "Creating a 2D space" should "initialize a world of the given size with just a player and no obstacles" in { - val size = (100, 150) - val space = Space2D(size) - space.world.player.position should be < size - space.world.obstacles.size shouldBe 0 - space.world.shape.width shouldBe size._1 - space.world.shape.height shouldBe size._2 - } - - "It" should "be possible to update the world" in {} - - "Checking collision between two 2D rectangular shapes" should "work" in { - val space = Space2D((10, 10)) - space.world.player.position shouldBe (5, 5) - space.world.player.shape shouldBe space.RectangularShape(1, 1) - val collidingObstacles = Set( - space.Obstacle((4, 3), (0, 0), space.RectangularShape(1, 2)), - space.Obstacle((5, 6), (0, 0), space.RectangularShape(1, 2)), - space.Obstacle((6, 5), (0, 0), space.RectangularShape(2, 1)), - ) - val nonCollidingObstacles = Set( - space.Obstacle((0, 0), (0, 0), space.RectangularShape(1, 2)), - space.Obstacle((7, 6), (0, 0), space.RectangularShape(3, 3)), - ) - space.world = space.World(space.world.player, collidingObstacles ++ nonCollidingObstacles, space.world.shape) - collidingObstacles.foreach(o => space.isCollidingWith(space.world.player)(o) shouldBe true) - nonCollidingObstacles.foreach(o => space.isCollidingWith(space.world.player)(o) shouldBe false) - } diff --git a/docs/content/_index.md b/docs/content/_index.md index f2ad3887..b447ec99 100644 --- a/docs/content/_index.md +++ b/docs/content/_index.md @@ -11,7 +11,7 @@ Analyze aspects such as: > - how and when to use one approach rather than the other > - any limitations and difficulties encountered in using them -## Roadmap +## Table of contents 1. [Boundary and break](./docs/01-boundaries) 2. [Blog posts service example: a direct-style vs monadic comparison](./docs/02-blog-posts-ws) diff --git a/docs/go.mod b/docs/go.mod index f82eccd3..d0a348ec 100644 --- a/docs/go.mod +++ b/docs/go.mod @@ -1,5 +1,5 @@ module github.com/tassiLuca/PPS-22-direct-style-experiments/docs -go 1.22.0 +go 1.21 require github.com/alex-shpak/hugo-book v0.0.0-20240217175702-a111041867b1 // indirect diff --git a/settings.gradle.kts b/settings.gradle.kts index adbcc3c1..3997cf22 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -20,7 +20,6 @@ include( ":blog-ws-monadic", ":rears-core", ":smart-home", - ":back-and-forth", ":analyzer-commons", ":analyzer-monadic", ":analyzer-direct",