From fdb1682273baed09f351705b266ee00302a3e156 Mon Sep 17 00:00:00 2001 From: Simon Grimme Date: Wed, 12 Oct 2022 13:23:39 +0200 Subject: [PATCH 1/3] Ignore empty folders and hidden files/folders in the library Hide ".gameyfin" folder on DOS systems (UNIX already worked) --- backend/pom.xml | 4 +-- .../gameyfin/config/FilesystemConfig.java | 28 +++++++++++++++++-- .../gameyfin/service/LibraryService.java | 25 ++++++++++++++++- frontend/package-lock.json | 4 +-- frontend/package.json | 2 +- frontend/pom.xml | 2 +- pom.xml | 2 +- 7 files changed, 57 insertions(+), 10 deletions(-) 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/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..5c05bff 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ de.grimsi gameyfin - 1.2.1 + 1.2.2-SNAPSHOT gameyfin gameyfin From 7582be7bc3c7c101187a8849b70146da30accdec Mon Sep 17 00:00:00 2001 From: Simon <9295182+grimsi@users.noreply.github.com> Date: Wed, 12 Oct 2022 18:39:13 +0200 Subject: [PATCH 2/3] Add CI pipeline Fixes #34 --- .github/workflows/build.yml | 34 ++++++++++++++++++++++++++++++++++ sonar-project.properties | 2 ++ 2 files changed, 36 insertions(+) create mode 100644 .github/workflows/build.yml create mode 100644 sonar-project.properties diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..2f63c29 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,34 @@ +name: Gameyfin CI Pipeline + +on: + push: + branches: + - master + pull_request: + types: [opened, synchronize, reopened] + +jobs: + build: + name: Build, Test & Scan + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - 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 + - name: SonarQube scan + uses: kitabisa/sonarqube-action@v1.2.0 + with: + host: ${{ secrets.SONARQUBE_HOST }} + login: ${{ secrets.SONARQUBE_TOKEN }} + projectKey: ${{ secrets.SONARQUBE_PROJECTKEY }} + - name: Upload build artifact + uses: actions/upload-artifact@v3 + with: + name: backend + path: backend/target/gameyfin-*.jar diff --git a/sonar-project.properties b/sonar-project.properties new file mode 100644 index 0000000..efe7579 --- /dev/null +++ b/sonar-project.properties @@ -0,0 +1,2 @@ +# Point SONAR to the compiled Java classes +sonar.java.binaries=./backend/target From c6c17002d4c90a20b7fb413db01d4c55b5d82661 Mon Sep 17 00:00:00 2001 From: Simon <9295182+grimsi@users.noreply.github.com> Date: Thu, 13 Oct 2022 17:01:08 +0200 Subject: [PATCH 3/3] Add release steps to CI pipeline --- .github/workflows/build.yml | 24 ++++++++++--- .github/workflows/release.yml | 65 +++++++++++++++++++++++++++++++++++ .maven_settings.xml | 10 ++++++ config/gameyfin.properties | 19 ++++++++++ pom.xml | 50 +++++++++++++++++++-------- sonar-project.properties | 2 ++ 6 files changed, 150 insertions(+), 20 deletions(-) create mode 100644 .github/workflows/release.yml create mode 100644 .maven_settings.xml create mode 100644 config/gameyfin.properties diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2f63c29..fd19a95 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -6,29 +6,43 @@ on: - master pull_request: types: [opened, synchronize, reopened] + workflow_dispatch: jobs: build: name: Build, Test & Scan runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 + 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: ${{ secrets.SONARQUBE_HOST }} + host: https://sonarqube.grimsi.de login: ${{ secrets.SONARQUBE_TOKEN }} - projectKey: ${{ secrets.SONARQUBE_PROJECTKEY }} + projectKey: grimsi_gameyfin_AYPM67pzsxiaNzCh9BZd + - name: Upload build artifact uses: actions/upload-artifact@v3 with: - name: backend + 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/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/pom.xml b/pom.xml index 5c05bff..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.2-SNAPSHOT - 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 index efe7579..cb0b4b5 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -1,2 +1,4 @@ +sonar.projectKey=grimsi_gameyfin_AYPM67pzsxiaNzCh9BZd + # Point SONAR to the compiled Java classes sonar.java.binaries=./backend/target