Bootstrapping development with some basic types and builds. (#1)
All checks were successful
/ Build and Release Application (push) Successful in 1m20s
All checks were successful
/ Build and Release Application (push) Successful in 1m20s
Reviewed-on: #1
This commit is contained in:
parent
dcf62239c6
commit
d10dcf4fb3
12 changed files with 450 additions and 1 deletions
40
.forgejo/workflows/pull_request.yaml
Normal file
40
.forgejo/workflows/pull_request.yaml
Normal file
|
@ -0,0 +1,40 @@
|
|||
on:
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened]
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
|
||||
jobs:
|
||||
library_snapshot:
|
||||
runs-on: docker
|
||||
container:
|
||||
image: registry.garrity.co:8443/gs/ci-scala:latest
|
||||
name: 'Build and Test Application Snapshot'
|
||||
env:
|
||||
GS_MAVEN_USER: ${{ vars.GS_MAVEN_USER }}
|
||||
GS_MAVEN_TOKEN: ${{ secrets.GS_MAVEN_TOKEN }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
name: 'Checkout Repository'
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: 'Pre-Commit'
|
||||
run: |
|
||||
pre-commit install
|
||||
pre-commit run --all-files
|
||||
- name: 'Prepare Versioned Build'
|
||||
run: |
|
||||
latest_git_tag="$(git describe --tags --abbrev=0 || echo 'No Tags')"
|
||||
latest_commit_message="$(git show -s --format=%s HEAD)"
|
||||
echo "Previous Git Tag: $latest_git_tag"
|
||||
echo "Latest Commit: $latest_commit_message (SNAPSHOT)"
|
||||
sbtn -Dsnapshot=true "api/calVerInfo"
|
||||
- name: 'Unit Tests and Code Coverage'
|
||||
run: |
|
||||
sbtn clean
|
||||
sbtn coverage
|
||||
sbtn test
|
||||
sbtn coverageReport
|
||||
sbtn coverageAggregate
|
53
.forgejo/workflows/release.yaml
Normal file
53
.forgejo/workflows/release.yaml
Normal file
|
@ -0,0 +1,53 @@
|
|||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
|
||||
jobs:
|
||||
application_release:
|
||||
runs-on: docker
|
||||
container:
|
||||
image: registry.garrity.co:8443/gs/ci-scala:latest
|
||||
name: 'Build and Release Application'
|
||||
env:
|
||||
GS_MAVEN_USER: ${{ vars.GS_MAVEN_USER }}
|
||||
GS_MAVEN_TOKEN: ${{ secrets.GS_MAVEN_TOKEN }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
name: 'Checkout Repository'
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: 'Pre-Commit'
|
||||
run: |
|
||||
pre-commit install
|
||||
pre-commit run --all-files
|
||||
- name: 'Prepare Versioned Build'
|
||||
run: |
|
||||
latest_git_tag="$(git describe --tags --abbrev=0 || echo 'No Tags')"
|
||||
latest_commit_message="$(git show -s --format=%s HEAD)"
|
||||
echo "Previous Git Tag: $latest_git_tag"
|
||||
echo "Latest Commit: $latest_commit_message"
|
||||
sbtn -Drelease=true api/calVerInfo
|
||||
- name: 'Unit Tests and Code Coverage'
|
||||
run: |
|
||||
sbtn clean
|
||||
sbtn coverage
|
||||
sbtn test
|
||||
sbtn coverageReport
|
||||
sbtn coverageAggregate
|
||||
- name: 'Publish Release'
|
||||
run: |
|
||||
sbtn coverageOff
|
||||
sbtn clean
|
||||
sbtn api/calVerWriteVersionToFile
|
||||
sbtn compile
|
||||
- name: 'Create Git Tag'
|
||||
run: |
|
||||
selected_version="$(cat .version)"
|
||||
echo "TAGGING IS CURRENTLY TURNED OFF -- $selected_version"
|
||||
#git tag "$selected_version"
|
||||
#git push origin "$selected_version"
|
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
target/
|
||||
project/target/
|
||||
project/project/
|
||||
.version
|
16
.pre-commit-config.yaml
Normal file
16
.pre-commit-config.yaml
Normal file
|
@ -0,0 +1,16 @@
|
|||
---
|
||||
repos:
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v4.5.0
|
||||
hooks:
|
||||
- id: end-of-file-fixer
|
||||
- id: trailing-whitespace
|
||||
- id: fix-byte-order-marker
|
||||
- id: mixed-line-ending
|
||||
args: ['--fix=lf']
|
||||
description: Enforces using only 'LF' line endings.
|
||||
- id: trailing-whitespace
|
||||
- repo: https://git.garrity.co/garrity-software/gs-pre-commit-scala
|
||||
rev: v1.0.1
|
||||
hooks:
|
||||
- id: scalafmt
|
72
.scalafmt.conf
Normal file
72
.scalafmt.conf
Normal file
|
@ -0,0 +1,72 @@
|
|||
// See: https://github.com/scalameta/scalafmt/tags for the latest tags.
|
||||
version = 3.8.1
|
||||
runner.dialect = scala3
|
||||
maxColumn = 80
|
||||
|
||||
rewrite {
|
||||
rules = [RedundantBraces, RedundantParens, Imports, SortModifiers]
|
||||
imports.expand = true
|
||||
imports.sort = scalastyle
|
||||
redundantBraces.ifElseExpressions = true
|
||||
redundantBraces.stringInterpolation = true
|
||||
}
|
||||
|
||||
indent {
|
||||
main = 2
|
||||
callSite = 2
|
||||
defnSite = 2
|
||||
extendSite = 4
|
||||
withSiteRelativeToExtends = 2
|
||||
commaSiteRelativeToExtends = 2
|
||||
}
|
||||
|
||||
align {
|
||||
preset = more
|
||||
openParenCallSite = false
|
||||
openParenDefnSite = false
|
||||
}
|
||||
|
||||
newlines {
|
||||
implicitParamListModifierForce = [before,after]
|
||||
topLevelStatementBlankLines = [
|
||||
{
|
||||
blanks = 1
|
||||
}
|
||||
]
|
||||
afterCurlyLambdaParams = squash
|
||||
}
|
||||
|
||||
danglingParentheses {
|
||||
defnSite = true
|
||||
callSite = true
|
||||
ctrlSite = true
|
||||
exclude = []
|
||||
}
|
||||
|
||||
verticalMultiline {
|
||||
atDefnSite = true
|
||||
arityThreshold = 2
|
||||
newlineAfterOpenParen = true
|
||||
}
|
||||
|
||||
comments {
|
||||
wrap = standalone
|
||||
}
|
||||
|
||||
docstrings {
|
||||
style = "SpaceAsterisk"
|
||||
oneline = unfold
|
||||
wrap = yes
|
||||
forceBlankLineBefore = true
|
||||
}
|
||||
|
||||
project {
|
||||
excludePaths = [
|
||||
"glob:**target/**",
|
||||
"glob:**.metals/**",
|
||||
"glob:**.bloop/**",
|
||||
"glob:**.bsp/**",
|
||||
"glob:**metals.sbt",
|
||||
"glob:**.git/**"
|
||||
]
|
||||
}
|
|
@ -8,7 +8,7 @@ Smol Kanban - dead simple tickets.
|
|||
## Why Smolban?
|
||||
|
||||
I created smolban after looking for a free issue tracker that suited my needs.
|
||||
I tend to prefer feature-light, and didn't want to install something larger
|
||||
I tend to prefer feature-light, and didn't want to install something larger
|
||||
or more "agile" focused. I explicitly do not want to use VCS issues. Smolban is
|
||||
a self-contained issue tracker intended to be easy to deploy and use.
|
||||
|
||||
|
|
98
build.sbt
Normal file
98
build.sbt
Normal file
|
@ -0,0 +1,98 @@
|
|||
val scala3: String = "3.4.1"
|
||||
|
||||
ThisBuild / scalaVersion := scala3
|
||||
ThisBuild / gsProjectName := "smolban"
|
||||
|
||||
ThisBuild / externalResolvers := Seq(
|
||||
"Garrity Software Mirror" at "https://maven.garrity.co/releases",
|
||||
"Garrity Software Releases" at "https://maven.garrity.co/gs"
|
||||
)
|
||||
|
||||
lazy val sharedSettings = Seq(
|
||||
scalaVersion := scala3,
|
||||
version := calVer.value,
|
||||
publish / skip := true,
|
||||
publishLocal / skip := true,
|
||||
publishArtifact := false
|
||||
)
|
||||
|
||||
val Deps = new {
|
||||
val Cats = new {
|
||||
val Core: ModuleID = "org.typelevel" %% "cats-core" % "2.10.0"
|
||||
val Effect: ModuleID = "org.typelevel" %% "cats-effect" % "3.5.4"
|
||||
}
|
||||
|
||||
val Fs2 = new {
|
||||
val Core: ModuleID = "co.fs2" %% "fs2-core" % "3.10.2"
|
||||
}
|
||||
|
||||
val Doobie = new {
|
||||
val Core: ModuleID = "org.tpolecat" %% "doobie-core" % "1.0.0-M5"
|
||||
}
|
||||
|
||||
val Http4s = new {
|
||||
val Core: ModuleID = "org.http4s" %% "http4s-core" % "1.0.0-M41"
|
||||
val Dsl: ModuleID = "org.http4s" %% "http4s-dsl" % "1.0.0-M41"
|
||||
val EmberServer: ModuleID =
|
||||
"org.http4s" %% "http4s-ember-server" % "1.0.0-M41"
|
||||
}
|
||||
|
||||
val Gs = new {
|
||||
val Uuid: ModuleID = "gs" %% "gs-uuid-v0" % "0.2.4"
|
||||
val Slug: ModuleID = "gs" %% "gs-slug-v0" % "0.1.3"
|
||||
val Config: ModuleID = "gs" %% "gs-config-v0" % "0.1.1"
|
||||
val Datagen: ModuleID = "gs" %% "gs-datagen-core-v0" % "0.1.1"
|
||||
}
|
||||
|
||||
val MUnit: ModuleID = "org.scalameta" %% "munit" % "1.0.0-RC1"
|
||||
}
|
||||
|
||||
lazy val testSettings = Seq(
|
||||
libraryDependencies ++= Seq(
|
||||
Deps.MUnit % Test,
|
||||
Deps.Gs.Datagen % Test
|
||||
)
|
||||
)
|
||||
|
||||
lazy val smolban = project
|
||||
.in(file("."))
|
||||
.aggregate(model, db, api)
|
||||
.settings(sharedSettings)
|
||||
.settings(name := s"${gsProjectName.value}")
|
||||
|
||||
lazy val model = project
|
||||
.in(file("modules/model"))
|
||||
.settings(sharedSettings)
|
||||
.settings(testSettings)
|
||||
.settings(name := s"${gsProjectName.value}-model")
|
||||
.settings(
|
||||
libraryDependencies ++= Seq(
|
||||
Deps.Gs.Uuid,
|
||||
Deps.Gs.Slug,
|
||||
Deps.Cats.Core
|
||||
)
|
||||
)
|
||||
|
||||
lazy val db = project
|
||||
.in(file("modules/db"))
|
||||
.settings(sharedSettings)
|
||||
.settings(testSettings)
|
||||
.settings(name := s"${gsProjectName.value}-db")
|
||||
.settings(
|
||||
libraryDependencies ++= Seq(
|
||||
Deps.Doobie.Core
|
||||
)
|
||||
)
|
||||
|
||||
lazy val api = project
|
||||
.in(file("modules/api"))
|
||||
.settings(sharedSettings)
|
||||
.settings(testSettings)
|
||||
.settings(name := s"${gsProjectName.value}-api")
|
||||
.settings(
|
||||
libraryDependencies ++= Seq(
|
||||
Deps.Http4s.Core,
|
||||
Deps.Http4s.Dsl,
|
||||
Deps.Http4s.EmberServer
|
||||
)
|
||||
)
|
|
@ -0,0 +1,34 @@
|
|||
package gs.smolban.model
|
||||
|
||||
import cats.Show
|
||||
import java.time.Instant
|
||||
|
||||
/** Describes an instant at which something was created. Opaque type for
|
||||
* `java.time.Instant`.
|
||||
*/
|
||||
opaque type CreatedAt = Instant
|
||||
|
||||
object CreatedAt:
|
||||
|
||||
/** Instantiate a new [[CreatedAt]].
|
||||
*
|
||||
* @param timestamp
|
||||
* The underlying timestamp.
|
||||
* @return
|
||||
* The new instance.
|
||||
*/
|
||||
def apply(timestamp: Instant): CreatedAt = timestamp
|
||||
|
||||
given CanEqual[CreatedAt, CreatedAt] = CanEqual.derived
|
||||
|
||||
given Show[CreatedAt] = _.toInstant().toString()
|
||||
|
||||
extension (createdAt: CreatedAt)
|
||||
/** Unwrap this value.
|
||||
*
|
||||
* @return
|
||||
* The underlying `Instant` value.
|
||||
*/
|
||||
def toInstant(): Instant = createdAt
|
||||
|
||||
end CreatedAt
|
54
modules/model/src/main/scala/gs/smolban/model/Group.scala
Normal file
54
modules/model/src/main/scala/gs/smolban/model/Group.scala
Normal file
|
@ -0,0 +1,54 @@
|
|||
package gs.smolban.model
|
||||
|
||||
import cats.Show
|
||||
import gs.slug.v0.Slug
|
||||
import gs.uuid.v0.UUID
|
||||
|
||||
/** Groups are the basic unit of organization in Smolban. Each [[Ticket]]
|
||||
* belongs to a single `Group`.
|
||||
*
|
||||
* @param id
|
||||
* The unique identifier for the group.
|
||||
* @param slug
|
||||
* The unique slug for the group.
|
||||
* @param createdAt
|
||||
* The instant at which this group was created.
|
||||
*/
|
||||
case class Group(
|
||||
id: Group.Id,
|
||||
slug: Slug,
|
||||
createdAt: CreatedAt
|
||||
)
|
||||
|
||||
object Group:
|
||||
|
||||
/** Unique identifier for a [[Group]]. This is an opaque type for a UUID.
|
||||
*/
|
||||
opaque type Id = UUID
|
||||
|
||||
object Id:
|
||||
|
||||
/** Instantiate a new [[Group.Id]].
|
||||
*
|
||||
* @param id
|
||||
* The underlying UUID.
|
||||
* @return
|
||||
* The new [[Group.Id]] instance.
|
||||
*/
|
||||
def apply(id: UUID): Id = id
|
||||
|
||||
given CanEqual[Id, Id] = CanEqual.derived
|
||||
|
||||
given Show[Id] = _.toUUID().withoutDashes()
|
||||
|
||||
extension (id: Id)
|
||||
/** Unwrap this Group ID.
|
||||
*
|
||||
* @return
|
||||
* The underlying UUID value.
|
||||
*/
|
||||
def toUUID(): UUID = id
|
||||
|
||||
end Id
|
||||
|
||||
end Group
|
44
modules/model/src/main/scala/gs/smolban/model/Ticket.scala
Normal file
44
modules/model/src/main/scala/gs/smolban/model/Ticket.scala
Normal file
|
@ -0,0 +1,44 @@
|
|||
package gs.smolban.model
|
||||
|
||||
import cats.Show
|
||||
|
||||
case class Ticket(
|
||||
id: Long,
|
||||
group: Group.Id,
|
||||
createdAt: CreatedAt
|
||||
)
|
||||
|
||||
object Ticket:
|
||||
|
||||
/** Unique identifier - relative to some [[Group]] - for a [[Ticket]]. This is
|
||||
* an opaque type for a Long. In general, [[Ticket]] identifiers are
|
||||
* sequences within a group.
|
||||
*/
|
||||
opaque type Id = Long
|
||||
|
||||
object Id:
|
||||
|
||||
/** Instantiate a new [[Ticket.Id]].
|
||||
*
|
||||
* @param id
|
||||
* The underlying Long.
|
||||
* @return
|
||||
* The new [[Ticket.Id]] instance.
|
||||
*/
|
||||
def apply(id: Long): Id = id
|
||||
|
||||
given CanEqual[Id, Id] = CanEqual.derived
|
||||
|
||||
given Show[Id] = _.toLong().toString()
|
||||
|
||||
extension (id: Id)
|
||||
/** Unwrap this Ticket ID.
|
||||
*
|
||||
* @return
|
||||
* The underlying Long value.
|
||||
*/
|
||||
def toLong(): Long = id
|
||||
|
||||
end Id
|
||||
|
||||
end Ticket
|
1
project/build.properties
Normal file
1
project/build.properties
Normal file
|
@ -0,0 +1 @@
|
|||
sbt.version=1.10.0
|
33
project/plugins.sbt
Normal file
33
project/plugins.sbt
Normal file
|
@ -0,0 +1,33 @@
|
|||
def selectCredentials(): Credentials =
|
||||
if ((Path.userHome / ".sbt" / ".credentials").exists())
|
||||
Credentials(Path.userHome / ".sbt" / ".credentials")
|
||||
else
|
||||
Credentials.apply(
|
||||
realm = "Reposilite",
|
||||
host = "maven.garrity.co",
|
||||
userName = sys.env
|
||||
.get("GS_MAVEN_USER")
|
||||
.getOrElse(
|
||||
throw new RuntimeException(
|
||||
"You must either provide ~/.sbt/.credentials or specify the GS_MAVEN_USER environment variable."
|
||||
)
|
||||
),
|
||||
passwd = sys.env
|
||||
.get("GS_MAVEN_TOKEN")
|
||||
.getOrElse(
|
||||
throw new RuntimeException(
|
||||
"You must either provide ~/.sbt/.credentials or specify the GS_MAVEN_TOKEN environment variable."
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
credentials += selectCredentials()
|
||||
|
||||
externalResolvers := Seq(
|
||||
"Garrity Software Mirror" at "https://maven.garrity.co/releases",
|
||||
"Garrity Software Releases" at "https://maven.garrity.co/gs"
|
||||
)
|
||||
|
||||
addSbtPlugin("org.scoverage" % "sbt-scoverage" % "2.0.8")
|
||||
addSbtPlugin("gs" % "sbt-garrity-software" % "0.2.0")
|
||||
addSbtPlugin("gs" % "sbt-gs-calver" % "0.2.0")
|
Loading…
Add table
Reference in a new issue