diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
new file mode 100644
index 0000000..fd19a95
--- /dev/null
+++ b/.github/workflows/build.yml
@@ -0,0 +1,48 @@
+name: Gameyfin CI Pipeline
+
+on:
+ push:
+ branches:
+ - master
+ pull_request:
+ types: [opened, synchronize, reopened]
+ workflow_dispatch:
+
+jobs:
+ build:
+ name: Build, Test & Scan
+ runs-on: ubuntu-latest
+ if: "!contains(github.event.head_commit.message, '[ci skip]')"
+ steps:
+ - name: Git checkout
+ uses: actions/checkout@v3
+
+ - name: Set up JDK
+ uses: actions/setup-java@v3
+ with:
+ java-version: '18'
+ distribution: 'temurin'
+ cache: 'maven'
+
+ - name: Extract Maven project version
+ id: project
+ run: echo "GAMEYFIN_VERSION=$(mvn help:evaluate -Dexpression=project.version -q -DforceStdout)" >> $GITHUB_OUTPUT
+
+ - name: Show extracted Maven project version
+ run: echo "${{ steps.project.outputs.GAMEYFIN_VERSION }}"
+
+ - name: Maven build
+ run: mvn --batch-mode --update-snapshots package
+
+ - name: SonarQube scan
+ uses: kitabisa/sonarqube-action@v1.2.0
+ with:
+ host: https://sonarqube.grimsi.de
+ login: ${{ secrets.SONARQUBE_TOKEN }}
+ projectKey: grimsi_gameyfin_AYPM67pzsxiaNzCh9BZd
+
+ - name: Upload build artifact
+ uses: actions/upload-artifact@v3
+ with:
+ name: gameyfin-${{ steps.project.outputs.GAMEYFIN_VERSION }}.jar
+ path: backend/target/gameyfin-*.jar
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
new file mode 100644
index 0000000..11002f4
--- /dev/null
+++ b/.github/workflows/release.yml
@@ -0,0 +1,65 @@
+name: Gameyfin Release
+
+on:
+ workflow_dispatch:
+ inputs:
+ branch:
+ description: "The branch to checkout when cutting the release."
+ required: true
+ default: "main"
+ releaseVersion:
+ description: "Default version to use when preparing a release."
+ required: true
+ default: "X.Y.Z"
+ developmentVersion:
+ description: "Default version to use for new local working copy."
+ required: true
+ default: "X.Y.Z-SNAPSHOT"
+
+jobs:
+ release:
+ runs-on: ubuntu-latest
+ name: Release
+ steps:
+ - name: Git checkout
+ uses: actions/checkout@v3
+ with:
+ ref: ${{ github.event.inputs.branch }}
+
+ - name: Set up JDK
+ uses: actions/setup-java@v3
+ with:
+ java-version: '18'
+ distribution: 'temurin'
+ cache: 'maven'
+
+ - name: Maven build
+ run: mvn --batch-mode --update-snapshots package -Dmaven.test.skip=true
+
+ - name: Configure Git User
+ run: |
+ git config user.email "actions@github.com"
+ git config user.name "GitHub Actions"
+
+ - name: Maven Release
+ run: mvn release:prepare release:perform -B -s .maven_settings.xml -DreleaseVersion=${{ github.event.inputs.releaseVersion }} -DdevelopmentVersion=${{ github.event.inputs.developmentVersion }}
+ env:
+ GITHUB_ACTOR: ${{ github.actor }}
+ GITHUB_TOKEN: ${{ github.token }}
+
+ - name: Git tag
+ uses: mathieudutour/github-tag-action@v6.0
+ with:
+ github_token: ${{ github.token }}
+ default_bump: false
+ custom_tag: ${{ github.event.inputs.releaseVersion }}
+
+ - name: Github Release
+ uses: "marvinpinto/action-automatic-releases@v1.2.1"
+ with:
+ repo_token: ${{ github.token }}
+ prerelease: false
+ files: |
+ LICENSE.md
+ backend/target/gameyfin-*.jar
+ config/gameyfin.properties
diff --git a/.maven_settings.xml b/.maven_settings.xml
new file mode 100644
index 0000000..2d0b3b8
--- /dev/null
+++ b/.maven_settings.xml
@@ -0,0 +1,10 @@
+
+
+
+
+ github
+ ${env.GITHUB_ACTOR}
+ ${env.GITHUB_TOKEN}
+
+
+
diff --git a/backend/pom.xml b/backend/pom.xml
index ad668cb..3aa49e7 100644
--- a/backend/pom.xml
+++ b/backend/pom.xml
@@ -7,7 +7,7 @@
gameyfin
de.grimsi
- 1.2.1
+ 1.2.2-SNAPSHOT
gameyfin-backend
@@ -17,7 +17,7 @@
18
- 1.6.9
+ 1.6.11
1.7.1
2.11.0
1.21
diff --git a/backend/src/main/java/de/grimsi/gameyfin/config/FilesystemConfig.java b/backend/src/main/java/de/grimsi/gameyfin/config/FilesystemConfig.java
index 10df1fc..54801b2 100644
--- a/backend/src/main/java/de/grimsi/gameyfin/config/FilesystemConfig.java
+++ b/backend/src/main/java/de/grimsi/gameyfin/config/FilesystemConfig.java
@@ -2,16 +2,25 @@ package de.grimsi.gameyfin.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
+import org.springframework.context.event.EventListener;
import org.springframework.core.env.*;
import org.springframework.util.PropertyPlaceholderHelper;
import org.springframework.util.StringUtils;
+import javax.annotation.PostConstruct;
import javax.sql.DataSource;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.LinkOption;
+import java.nio.file.Path;
+import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Properties;
import java.util.stream.StreamSupport;
@@ -19,6 +28,8 @@ import java.util.stream.StreamSupport;
@Configuration
public class FilesystemConfig {
+ private static final String INTERNAL_FOLDER_NAME = ".gameyfin";
+
@Value("#{'${gameyfin.sources}'.split(',')[0]}")
private String firstLibraryPath;
@@ -31,16 +42,29 @@ public class FilesystemConfig {
@Autowired
Environment env;
+ /**
+ * This will make sure that the internal folder (".gameyfin") is marked as hidden on DOS/Windows-based systems.
+ * On UNIX-based systems files and folders starting with a dot are hidden
+ */
+ @EventListener(ApplicationReadyEvent.class)
+ public void hideInternalFolderOnDOS() throws IOException {
+ Path internalFolder = Paths.get("%s/%s".formatted(firstLibraryPath, INTERNAL_FOLDER_NAME));
+
+ if(!Files.exists(internalFolder) || !Files.isDirectory(internalFolder)) return;
+
+ Files.setAttribute(internalFolder, "dos:hidden", Boolean.TRUE, LinkOption.NOFOLLOW_LINKS);
+ }
+
@Autowired
public void setConfigurableEnvironment(ConfigurableEnvironment env) {
Properties props = new Properties();
if(!StringUtils.hasText(dbPath)) {
- props.setProperty("gameyfin.db", "%s/.gameyfin/db".formatted(firstLibraryPath));
+ props.setProperty("gameyfin.db", "%s/%s/db".formatted(firstLibraryPath, INTERNAL_FOLDER_NAME));
}
if(!StringUtils.hasText(cachePath)) {
- props.setProperty("gameyfin.cache", "%s/.gameyfin/cache".formatted(firstLibraryPath));
+ props.setProperty("gameyfin.cache", "%s/%s/cache".formatted(firstLibraryPath, INTERNAL_FOLDER_NAME));
}
env.getPropertySources().addFirst(new PropertiesPropertySource("gameyfinFilesystemProperties", props));
diff --git a/backend/src/main/java/de/grimsi/gameyfin/service/LibraryService.java b/backend/src/main/java/de/grimsi/gameyfin/service/LibraryService.java
index a171e0e..9ecf99d 100644
--- a/backend/src/main/java/de/grimsi/gameyfin/service/LibraryService.java
+++ b/backend/src/main/java/de/grimsi/gameyfin/service/LibraryService.java
@@ -16,8 +16,10 @@ import org.springframework.stereotype.Service;
import org.springframework.util.StopWatch;
import java.io.IOException;
+import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
+import java.nio.file.Paths;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
@@ -46,7 +48,28 @@ public class LibraryService {
folder -> {
try (Stream stream = Files.list(folder)) {
// return all sub-folders (non-recursive) and files that have an extension that indicates that they are a downloadable file
- List gameFilesFromThisFolder = stream.filter(p -> Files.isDirectory(p) || hasGameArchiveExtension(p)).toList();
+ List gameFilesFromThisFolder = stream
+ .filter(p -> Files.isDirectory(p) || hasGameArchiveExtension(p))
+ // filter out all hidden files and folders
+ .filter(p -> {
+ try {
+ return !(Files.isHidden(p));
+ } catch (IOException e) {
+ throw new RuntimeException("Error while checking if '%s' is hidden.".formatted(p), e);
+ }
+ })
+ // filter out all empty directories
+ .filter(p -> {
+ if(!Files.isDirectory(p)) return true;
+
+ try(DirectoryStream s = Files.newDirectoryStream(p)) {
+ return s.iterator().hasNext();
+ } catch(IOException e) {
+ throw new RuntimeException("Error while checking if folder '%s' is empty.".formatted(p), e);
+ }
+ })
+ .toList();
+
gamefiles.addAll(gameFilesFromThisFolder);
} catch (IOException e) {
diff --git a/config/gameyfin.properties b/config/gameyfin.properties
new file mode 100644
index 0000000..eeeae45
--- /dev/null
+++ b/config/gameyfin.properties
@@ -0,0 +1,19 @@
+# Uncomment if you want to change the port from 8080
+# server.port=8081
+
+# Gameyfin admin interface username and password
+gameyfin.user=
+gameyfin.password=
+
+# Gameyfin source folders
+gameyfin.sources=
+
+# Uncomment if you want to specify the Gameyfin database path (default is /.gameyfin/db)
+# gameyfin.db=
+
+# Uncomment if you want to specify the Gameyfin cache path (default is /.gameyfin/cache)
+# gameyfin.cache=
+
+# Your twitch client-id and client-secret
+gameyfin.igdb.api.client-id=
+gameyfin.igdb.api.client-secret=
\ No newline at end of file
diff --git a/frontend/package-lock.json b/frontend/package-lock.json
index 5d66472..7f2a857 100644
--- a/frontend/package-lock.json
+++ b/frontend/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "frontend",
- "version": "1.2.1",
+ "version": "1.2.2-SNAPSHOT",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "frontend",
- "version": "1.2.1",
+ "version": "1.2.2-SNAPSHOT",
"dependencies": {
"@angular/animations": "^14.0.0",
"@angular/cdk": "^14.1.0",
diff --git a/frontend/package.json b/frontend/package.json
index e6137b3..bfe88ad 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -1,6 +1,6 @@
{
"name": "frontend",
- "version": "1.2.1",
+ "version": "1.2.2-SNAPSHOT",
"scripts": {
"ng": "ng",
"start": "ng serve",
diff --git a/frontend/pom.xml b/frontend/pom.xml
index d3e8103..a187801 100644
--- a/frontend/pom.xml
+++ b/frontend/pom.xml
@@ -5,7 +5,7 @@
gameyfin
de.grimsi
- 1.2.1
+ 1.2.2-SNAPSHOT
4.0.0
diff --git a/pom.xml b/pom.xml
index b100e7d..6a952d1 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1,25 +1,45 @@
- 4.0.0
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
+ 4.0.0
- de.grimsi
- gameyfin
- 1.2.1
- gameyfin
- gameyfin
+ de.grimsi
+ gameyfin
+ 1.2.2-SNAPSHOT
+ gameyfin
+ gameyfin
- pom
+ pom
- frontend
+ frontend
backend
-
+
- org.springframework.boot
- spring-boot-starter-parent
- 2.7.1
-
-
+ org.springframework.boot
+ spring-boot-starter-parent
+ 2.7.1
+
+
+
+
+ scm:git:https://github.com/grimsi/gameyfin.git
+ scm:git:https://github.com/grimsi/gameyfin.git
+ scm:git:https://github.com/grimsi/gameyfin.git
+ HEAD
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-release-plugin
+ 2.5.3
+
+ [ci skip]
+
+
+
+
diff --git a/sonar-project.properties b/sonar-project.properties
new file mode 100644
index 0000000..cb0b4b5
--- /dev/null
+++ b/sonar-project.properties
@@ -0,0 +1,4 @@
+sonar.projectKey=grimsi_gameyfin_AYPM67pzsxiaNzCh9BZd
+
+# Point SONAR to the compiled Java classes
+sonar.java.binaries=./backend/target