From d307e87d1a1101d9a13945476a60f912a84e126a Mon Sep 17 00:00:00 2001 From: memo Date: Fri, 20 Dec 2024 09:45:39 +0100 Subject: [PATCH] add metadataIssueUrl to package and channel json files --- CHANGELOG.md | 1 + .../shared/src/main/scala/sc4pac/shared.scala | 4 ++- src/main/scala/sc4pac/ChannelUtil.scala | 33 ++++++++++++++----- src/main/scala/sc4pac/cli.scala | 19 +++++++---- .../main/scala/sc4pac/web/ChannelPage.scala | 5 ++- 5 files changed, 42 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d332d8..65bbf09 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ - The auto-shutdown functionality of the server now handles multiple connections and page reloads of the web-app ([#10][gui10]). ### Changed +- The channels now includes a URL for creating a new GH issue associated to a specific package (derived from `--metadata-source-url`). - API upgrade to 2.1: - `/update` accepts a new parameter `refreshChannels` to clear cached data ([#14][gui14]). - New `/packages.open` endpoint for externally instructing the GUI to open a specific package page (#21). diff --git a/shared/shared/src/main/scala/sc4pac/shared.scala b/shared/shared/src/main/scala/sc4pac/shared.scala index 3ecf977..10507d3 100644 --- a/shared/shared/src/main/scala/sc4pac/shared.scala +++ b/shared/shared/src/main/scala/sc4pac/shared.scala @@ -154,6 +154,7 @@ abstract class SharedData { variantDescriptions: Map[String, Map[String, String]] = Map.empty, // variantKey -> variantValue -> description metadataSource: Option[SubPath] = None, // path to yaml file metadataSourceUrl: Option[Uri] = None, // full URL to yaml file + metadataIssueUrl: Option[Uri] = None, // URL to create new issue for this package channelLabel: Option[String] = None, ) extends PackageAsset { @@ -332,9 +333,10 @@ abstract class SharedData { case class Info( channelLabel: Option[String], metadataSourceUrl: Option[Uri], + metadataIssueUrl: Option[Uri] = None, ) derives ReadWriter object Info { - val empty = Info(None, None) + val empty = Info(None, None, None) } case class ExtPkg(group: String, name: String, checksum: Checksum) derives ReadWriter { diff --git a/src/main/scala/sc4pac/ChannelUtil.scala b/src/main/scala/sc4pac/ChannelUtil.scala index eb9ab95..e6ba481 100644 --- a/src/main/scala/sc4pac/ChannelUtil.scala +++ b/src/main/scala/sc4pac/ChannelUtil.scala @@ -44,20 +44,35 @@ object ChannelUtil { // validate that variants form a DecisionTree Sc4pac.DecisionTree.fromVariants(variants2.map(_.variant)) match { case Left(errStr) => ZIO.fail(errStr) - case Right(_) => ZIO.serviceWith[JD.Channel.Info] { channelInfo => JD.Package( - group = group, name = name, version = version, subfolder = subfolder, info = info, - variants = variants2, - variantDescriptions = variantDescriptions, - metadataSource = metadataSource, // kept for backward compatibility - metadataSourceUrl = + case Right(_) => ZIO.serviceWith[JD.Channel.Info] { channelInfo => + val metadataSourceUrl = for { baseUri <- channelInfo.metadataSourceUrl path <- metadataSource - } yield resolveMetadataSourceUrl(baseUri, path), - channelLabel = channelInfo.channelLabel, - )} + } yield resolveMetadataSourceUrl(baseUri, path) + JD.Package( + group = group, name = name, version = version, subfolder = subfolder, info = info, + variants = variants2, + variantDescriptions = variantDescriptions, + metadataSource = metadataSource, // kept for backward compatibility + metadataSourceUrl = metadataSourceUrl, + metadataIssueUrl = channelInfo.metadataIssueUrl.filter(_.getHost == "github.com").map(newIssueUrl(_, metadataSourceUrl)), + channelLabel = channelInfo.channelLabel, + ) + } } } + + private def newIssueUrl(metadataIssueUrl: java.net.URI, metadataSourceUrl: Option[java.net.URI]): java.net.URI = { + val module = BareModule(Organization(group), ModuleName(name)).orgName + val link = metadataSourceUrl.map(u => s"[`$module`]($u)").getOrElse(s"`$module`") + val websiteLink = if (info.website.nonEmpty) s" from ${info.website}" else "" + val q = zio.http.QueryParams( + "title" -> s"[$module] New bug report", + "body" -> s"Package: $link$websiteLink\n\nDescribe the problem hereā€¦", + ).encode(java.nio.charset.StandardCharsets.UTF_8) + metadataIssueUrl.resolve(s"issues/new$q") + } } case class YamlAsset( diff --git a/src/main/scala/sc4pac/cli.scala b/src/main/scala/sc4pac/cli.scala index 1377abb..cf7be89 100644 --- a/src/main/scala/sc4pac/cli.scala +++ b/src/main/scala/sc4pac/cli.scala @@ -476,15 +476,20 @@ object Commands { args.all match { case Nil => error(caseapp.core.Error.Other("An argument is needed: YAML input directory")) case inputs => + val metadataSourceUrl = Option(options.metadataSourceUrl).filter(_.nonEmpty) + .map(MetadataRepository.parseChannelUrl) + .map { + case Left(err) => error(caseapp.core.Error.Other(s"Malformed metadata source URL: $err")) + case Right(uri) => uri + } + val ghUrl = "^https://github.com/([^/]+/[^/]+)/.*".r // matches repo val info = JD.Channel.Info( channelLabel = Option(options.label).filter(_.nonEmpty), - metadataSourceUrl = - Option(options.metadataSourceUrl).filter(_.nonEmpty) - .map(MetadataRepository.parseChannelUrl) - .map { - case Left(err) => error(caseapp.core.Error.Other(s"Malformed metadata source URL: $err")) - case Right(uri) => uri - }, + metadataSourceUrl = metadataSourceUrl, + metadataIssueUrl = metadataSourceUrl.flatMap(_.toString match { + case ghUrl(repo) => Some(java.net.URI.create(s"https://github.com/$repo/issues")) + case _ => None + }), ) val task = ChannelUtil.convertYamlToJson(inputs.map(os.Path(_, os.pwd)), os.Path(options.output, os.pwd)) diff --git a/web/src/main/scala/sc4pac/web/ChannelPage.scala b/web/src/main/scala/sc4pac/web/ChannelPage.scala index 34b3b39..55821d5 100644 --- a/web/src/main/scala/sc4pac/web/ChannelPage.scala +++ b/web/src/main/scala/sc4pac/web/ChannelPage.scala @@ -68,7 +68,6 @@ object ChannelPage { // val sc4pacUrl = "https://github.com/memo33/sc4pac-tools#sc4pac" val sc4pacUrl = "https://memo33.github.io/sc4pac/#/" val sc4pacGuiUrl = "https://github.com/memo33/sc4pac-gui/releases" - val issueUrl = "https://github.com/memo33/sc4pac/issues" lazy val backend = sttp.client4.fetch.FetchBackend() @@ -206,8 +205,8 @@ object ChannelPage { H.div( H.div(H.float := "right")( - pkg.metadataSourceUrl.toSeq.map(yamlUrl => H.a(H.cls := "btn", H.href := yamlUrl.toString)("Edit metadata")) - :+ H.a(H.cls := "btn", H.href := issueUrl)("Report a problem") + pkg.metadataSourceUrl.toSeq.map(url => H.a(H.cls := "btn", H.href := url.toString)("Show metadata")) + ++ pkg.metadataIssueUrl.map(url => H.a(H.cls := "btn", H.href := url.toString)("Report a problem")) ), H.h2(H.clear := "right")(module.orgName), H.table(H.id := "pkginfo")(H.tbody(b.result())),