Merge pull request #21 from xTVaser/v/actions

actions: add build and linter action
This commit is contained in:
tripp 2022-04-16 13:44:14 -04:00 committed by GitHub
commit f43b62243a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 11824 additions and 125 deletions

48
.github/workflows/build.yaml vendored Normal file
View file

@ -0,0 +1,48 @@
name: Build
on:
push:
branches:
- main
tags:
- v*
pull_request:
branches:
- main
jobs:
tauri:
strategy:
fail-fast: false
matrix:
platform: [ubuntu-latest, windows-latest]
runs-on: ${{ matrix.platform }}
steps:
- uses: actions/checkout@v2
- name: setup node
uses: actions/setup-node@v1
with:
node-version: 16
- name: install Rust stable
uses: actions-rs/toolchain@v1
with:
toolchain: stable
- name: install dependencies (ubuntu only)
if: matrix.platform == 'ubuntu-latest'
run: |
sudo apt-get update
sudo apt-get install -y libgtk-3-dev webkit2gtk-4.0 libappindicator3-dev librsvg2-dev patchelf
- name: install app dependencies and build it
run: |
npm ci
npm run mock-bin
npm run build
- uses: tauri-apps/tauri-action@v0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

30
.github/workflows/lint.yaml vendored Normal file
View file

@ -0,0 +1,30 @@
name: Linter
on:
push:
branches:
- main
pull_request:
branches:
- main
jobs:
tauri:
strategy:
fail-fast: false
matrix:
platform: [ubuntu-latest]
runs-on: ${{ matrix.platform }}
steps:
- uses: actions/checkout@v2
- name: setup node
uses: actions/setup-node@v1
with:
node-version: 16
- name: install dependencies and check formatting
run: |
npm ci
npm run lint

4
.prettierignore Normal file
View file

@ -0,0 +1,4 @@
src-tauri/
node_modules/
dist/
bundle-test/

0
.prettierrc.json Normal file
View file

View file

@ -1,24 +1,28 @@
# OpenGOAL Launcher
## Description
A launcher for users to install and run the OpenGOAL project with ease
## Preview
![Launcher Preview](./docs//screenshots/screenshot.png)
## Disclaimer
Users are required to provide their own copy of the ISO file in order to run the game.
## Resources
- [OpenGOAL Github Organization](https://github.com/open-goal/)
- [OpenGOAL Documentation](https://open-goal.github.io/)
- [OpenGOAL Discord](https://discord.gg/twBEFbMnqw)
## Development
We are using Tauri to build a native app, but still with simple Web technology. You will need to setup the prerequesites using the instructions here https://tauri.studio/docs/getting-started/prerequisites
We are using Tauri to build a native app, but still with simple Web technology. You will need to setup the prerequesites using the instructions here https://tauri.studio/docs/getting-started/prerequisites
> Additionally, this presumes your environment has WebView2 (windows) or webkit2 (linux) already available. This is a requirement for end-users as well! Many modern OSes already ship with such a thing, but it's something we'll need to investigate.
> Additionally, this presumes your environment has WebView2 (windows) or webkit2 (linux) already available. This is a requirement for end-users as well! Many modern OSes already ship with such a thing, but it's something we'll need to investigate.
- `npm install`
- `npm run tauri dev`

View file

@ -47,4 +47,4 @@ ul {
text-decoration: none;
color: white;
margin: 10px 0;
}
}

View file

@ -1,4 +1,4 @@
import './settings.css';
import "./settings.css";
export const actions = `<div class="actions">
<div class="general nav-item" data-tooltip="General">
@ -71,4 +71,4 @@ export const links_pane = `<div class="pane">
<i class="bi bi-discord"> Discord Server</i>
</a>
</div>
</div>`;
</div>`;

View file

@ -3,8 +3,11 @@
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.8.1/font/bootstrap-icons.css">
<link rel="stylesheet" href="/src/css/style.css">
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.8.1/font/bootstrap-icons.css"
/>
<link rel="stylesheet" href="/src/css/style.css" />
<title>OpenGOAL Launcher</title>
</head>
<body>

11528
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -10,13 +10,17 @@
"tauri": "tauri",
"tauricon": "tauricon",
"package": "node ./scripts/rename-binaries.js",
"update-bin": "node ./scripts/update-binaries.js && npm run package"
"update-bin": "node ./scripts/update-binaries.js && npm run package",
"lint": "npx prettier --check .",
"format": "npx prettier --write .",
"mock-bin": "node ./scripts/dummy-binaries.js"
},
"devDependencies": {
"@sveltejs/vite-plugin-svelte": "^1.0.0-next.30",
"@tauri-apps/cli": "^1.0.0-rc.8",
"@tauri-apps/tauricon": "github:tauri-apps/tauricon",
"execa": "^6.1.0",
"prettier": "2.6.2",
"svelte": "^3.44.0",
"vite": "^2.9.2"
},

22
scripts/dummy-binaries.js Normal file
View file

@ -0,0 +1,22 @@
/**
* Used for builds / CI to basically mock the install process
*
* If Tauri can't find the binaries, it will fail (maybe there is a better way to ignore this though)
*/
import { existsSync, rmdirSync, mkdirSync, writeFileSync } from "fs";
// Clear our current binaries
if (existsSync("./src-tauri/bin")) {
rmdirSync("./src-tauri/bin", { recursive: true, force: true });
}
// Recreate the directory
mkdirSync("./src-tauri/bin");
// Create empty executables
let extension = "";
if (process.platform === "win32") {
extension = ".exe";
}
writeFileSync(`./src-tauri/bin/extractor${extension}`, "dummy");
writeFileSync(`./src-tauri/bin/gk${extension}`, "dummy");
writeFileSync(`./src-tauri/bin/goalc${extension}`, "dummy");

View file

@ -3,40 +3,40 @@
* When `tauri build` is ran, it looks for the binary name appended with the platform specific postfix.
*/
import {execa} from 'execa';
import { existsSync, renameSync } from 'fs'
import { execa } from "execa";
import { existsSync, renameSync } from "fs";
let extension = ''
if (process.platform === 'win32') {
extension = '.exe'
let extension = "";
if (process.platform === "win32") {
extension = ".exe";
}
async function main() {
const rustInfo = (await execa('rustc', ['-vV'])).stdout
const targetTriple = /host: (\S+)/g.exec(rustInfo)[1]
const rustInfo = (await execa("rustc", ["-vV"])).stdout;
const targetTriple = /host: (\S+)/g.exec(rustInfo)[1];
if (!targetTriple) {
console.error('Failed to determine platform target triple')
console.error("Failed to determine platform target triple");
}
if (existsSync(`src-tauri/bin/extractor${extension}`)) {
renameSync(
`src-tauri/bin/extractor${extension}`,
`src-tauri/bin/extractor-${targetTriple}${extension}`
)
);
}
if (existsSync(`src-tauri/bin/gk${extension}`)) {
renameSync(
`src-tauri/bin/gk${extension}`,
`src-tauri/bin/gk-${targetTriple}${extension}`
)
);
}
if (existsSync(`src-tauri/bin/goalc${extension}`)) {
renameSync(
`src-tauri/bin/goalc${extension}`,
`src-tauri/bin/goalc-${targetTriple}${extension}`
)
);
}
}
main().catch((e) => {
throw e
})
throw e;
});

View file

@ -5,16 +5,25 @@
* Assumes that `jak-project` is one directory up
*/
import { existsSync, rmdirSync, mkdirSync, copyFileSync } from 'fs';
import { existsSync, rmdirSync, mkdirSync, copyFileSync } from "fs";
// Clear our current binaries
if (existsSync("./src-tauri/bin")) {
rmdirSync("./src-tauri/bin", {recursive: true, force: true});
rmdirSync("./src-tauri/bin", { recursive: true, force: true });
}
// Recreate the directory
mkdirSync("./src-tauri/bin");
// Copy over the necessary binaries
// - Assumes Windows!
copyFileSync("../jak-project/out/build/Release/bin/extractor.exe", "./src-tauri/bin/extractor.exe");
copyFileSync("../jak-project/out/build/Release/bin/gk.exe", "./src-tauri/bin/gk.exe");
copyFileSync("../jak-project/out/build/Release/bin/goalc.exe", "./src-tauri/bin/goalc.exe");
copyFileSync(
"../jak-project/out/build/Release/bin/extractor.exe",
"./src-tauri/bin/extractor.exe"
);
copyFileSync(
"../jak-project/out/build/Release/bin/gk.exe",
"./src-tauri/bin/gk.exe"
);
copyFileSync(
"../jak-project/out/build/Release/bin/goalc.exe",
"./src-tauri/bin/goalc.exe"
);

View file

@ -1,29 +1,29 @@
* {
-webkit-user-drag: none;
font-family: 'Oswald', sans-serif;
font-family: "Oswald", sans-serif;
}
:root {
--bg-blue: #1B202B;
--bg-blue: #1b202b;
}
@font-face {
font-family: 'AdventPro';
font-family: "AdventPro";
font-style: normal;
font-weight: 400;
src: url('/src/assets/fonts/AdventPro-Regular.ttf');
src: url("/src/assets/fonts/AdventPro-Regular.ttf");
}
@font-face {
font-family: 'AdventPro';
font-family: "AdventPro";
font-style: normal;
font-weight: 700;
src: url('/src/assets/fonts/AdventPro-Bold.ttf');
src: url("/src/assets/fonts/AdventPro-Bold.ttf");
}
@font-face {
font-family: 'Oswald';
src: url('/src/assets/fonts/Oswald-VariableFont_wght.ttf');
font-family: "Oswald";
src: url("/src/assets/fonts/Oswald-VariableFont_wght.ttf");
}
body {
@ -101,7 +101,8 @@ body {
cursor: pointer;
}
.nav-item.disabled:hover, .nav-item.disabled {
.nav-item.disabled:hover,
.nav-item.disabled {
cursor: not-allowed;
filter: grayscale(100%);
}
@ -116,17 +117,17 @@ body {
}
.nav-item a::before {
padding: .5rem;
padding: 0.5rem;
width: max-content;
background: #333;
color: white;
content: attr(data-tooltip);
border-radius: .3rem;
border-radius: 0.3rem;
text-align: center;
}
.nav-item a:hover::before {
--scale: 1
--scale: 1;
}
.nav-item img {
@ -173,7 +174,7 @@ body {
.btn {
text-align: center;
background: linear-gradient(to top, #F3B33F, #FBEC4F);
background: linear-gradient(to top, #f3b33f, #fbec4f);
border: none;
padding: 0.25em;
padding-left: 1em;
@ -205,11 +206,8 @@ body {
.btn:enabled:hover {
cursor: pointer;
color: orange;
text-shadow:
-1px -1px 0 #000,
1px -1px 0 #000,
-1px 1px 0 #000,
1px 1px 0 #000;
text-shadow: -1px -1px 0 #000, 1px -1px 0 #000, -1px 1px 0 #000,
1px 1px 0 #000;
}
.btn:disabled {
@ -233,8 +231,12 @@ body {
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
.progress-row {

View file

@ -1,15 +1,15 @@
import { createDir, readTextFile, writeFile } from '@tauri-apps/api/fs';
import { appDir, join } from '@tauri-apps/api/path';
import { Store } from 'tauri-plugin-store-api';
import { createDir, readTextFile, writeFile } from "@tauri-apps/api/fs";
import { appDir, join } from "@tauri-apps/api/path";
import { Store } from "tauri-plugin-store-api";
export class SupportedGame {
static Jak1 = new SupportedGame("Jak 1")
static Jak2 = new SupportedGame("Jak 2")
static Jak3 = new SupportedGame("Jak 3")
static JakX = new SupportedGame("Jak X")
static Jak1 = new SupportedGame("Jak 1");
static Jak2 = new SupportedGame("Jak 2");
static Jak3 = new SupportedGame("Jak 3");
static JakX = new SupportedGame("Jak X");
constructor(name) {
this.name = name
this.name = name;
}
static fromName(name) {
@ -29,25 +29,28 @@ export class SupportedGame {
}
const key_lastActiveGame = "lastActiveGame";
const store = new Store('settings.json');
const store = new Store("settings.json");
export async function initConfig() {
const path = await join(await appDir(), 'settings.json');
const path = await join(await appDir(), "settings.json");
try {
await readTextFile(path);
} catch (error) {
console.log('[Launcher]: Settings file not found or could not be loaded!');
console.log("[Launcher]: Settings file not found or could not be loaded!");
await createDir(await appDir(), { recursive: true });
const initSettings = {
[SupportedGame.Jak1.name]: { "installed": false },
[SupportedGame.Jak2.name]: { "installed": false },
[SupportedGame.Jak3.name]: { "installed": false },
[key_lastActiveGame]: SupportedGame.Jak1.name
[SupportedGame.Jak1.name]: { installed: false },
[SupportedGame.Jak2.name]: { installed: false },
[SupportedGame.Jak3.name]: { installed: false },
[key_lastActiveGame]: SupportedGame.Jak1.name,
};
await writeFile({ contents: JSON.stringify(initSettings, null, 2), path: path });
console.log('[Launcher]: Settings file initialized');
await writeFile({
contents: JSON.stringify(initSettings, null, 2),
path: path,
});
console.log("[Launcher]: Settings file initialized");
}
};
}
/**
*
@ -57,7 +60,7 @@ export async function initConfig() {
export async function getInstallStatus(supportedGame) {
await store.load();
const val = await store.get(supportedGame.name);
if (val == null || !'installed' in val) {
if (val == null || !"installed" in val) {
return false;
}
return val.installed;
@ -82,6 +85,6 @@ export async function getLastActiveGame() {
*/
export async function setInstallStatus(supportedGame, installed) {
await store.load();
await store.set(supportedGame.name, { "installed": installed });
await store.set(supportedGame.name, { installed: installed });
await store.save();
}

View file

@ -1,5 +1,5 @@
import { Command } from '@tauri-apps/api/shell';
import { resourceDir } from '@tauri-apps/api/path';
import { Command } from "@tauri-apps/api/shell";
import { resourceDir } from "@tauri-apps/api/path";
// TODO - is this set to `production` properly in release mode?
function isInDebugMode() {
@ -10,16 +10,22 @@ function isInDebugMode() {
let debugPath;
if (isInDebugMode()) {
let path = await resourceDir();
debugPath = path.split("launcher")[0].split("?\\")[1]
debugPath = path.split("launcher")[0].split("?\\")[1];
debugPath += "\\launcher\\bundle-test\\data";
}
export async function launchGame() {
let command;
if (isInDebugMode()) {
command = Command.sidecar('bin/gk', ["-boot", "-fakeiso", '-debug', '-proj-path', debugPath], { cwd: 'bin' })
command = Command.sidecar(
"bin/gk",
["-boot", "-fakeiso", "-debug", "-proj-path", debugPath],
{ cwd: "bin" }
);
} else {
command = Command.sidecar('bin/gk', ["-boot", "-fakeiso", "-debug"], { cwd: 'bin' });
command = Command.sidecar("bin/gk", ["-boot", "-fakeiso", "-debug"], {
cwd: "bin",
});
}
command.execute();
}

View file

@ -1,5 +1,5 @@
import { Command } from '@tauri-apps/api/shell';
import { resourceDir } from '@tauri-apps/api/path';
import { Command } from "@tauri-apps/api/shell";
import { resourceDir } from "@tauri-apps/api/path";
// TODO - is this set to `production` properly in release mode?
function isInDebugMode() {
@ -10,7 +10,7 @@ function isInDebugMode() {
let debugPath;
if (isInDebugMode()) {
let path = await resourceDir();
debugPath = path.split("launcher")[0].split("?\\")[1]
debugPath = path.split("launcher")[0].split("?\\")[1];
debugPath += "\\launcher\\bundle-test\\data";
}
@ -21,9 +21,15 @@ if (isInDebugMode()) {
export async function extractISO(filePath) {
let command;
if (isInDebugMode()) {
command = Command.sidecar('bin/extractor', [filePath, '--extract', '--proj-path', debugPath], { cwd: 'bin' })
command = Command.sidecar(
"bin/extractor",
[filePath, "--extract", "--proj-path", debugPath],
{ cwd: "bin" }
);
} else {
command = Command.sidecar('bin/extractor', [filePath, '--extract'], { cwd: 'bin' });
command = Command.sidecar("bin/extractor", [filePath, "--extract"], {
cwd: "bin",
});
}
return await command.execute();
@ -33,12 +39,18 @@ export async function extractISO(filePath) {
* @param {String} filePath
* @returns {Promise<ChildProcess>}
*/
export async function validateGameData(filePath) {
export async function validateGameData(filePath) {
let command;
if (isInDebugMode()) {
command = Command.sidecar('bin/extractor', [filePath, '--validate', '--proj-path', debugPath], { cwd: 'bin' })
command = Command.sidecar(
"bin/extractor",
[filePath, "--validate", "--proj-path", debugPath],
{ cwd: "bin" }
);
} else {
command = Command.sidecar('bin/extractor', [filePath, '--validate'], { cwd: 'bin' });
command = Command.sidecar("bin/extractor", [filePath, "--validate"], {
cwd: "bin",
});
}
return await command.execute();
@ -48,12 +60,18 @@ export async function extractISO(filePath) {
* @param {String} filePath
* @returns {Promise<ChildProcess>}
*/
export async function decompileGameData(filePath) {
export async function decompileGameData(filePath) {
let command;
if (isInDebugMode()) {
command = Command.sidecar('bin/extractor', [filePath, '--decompile', '--proj-path', debugPath], { cwd: 'bin' })
command = Command.sidecar(
"bin/extractor",
[filePath, "--decompile", "--proj-path", debugPath],
{ cwd: "bin" }
);
} else {
command = Command.sidecar('bin/extractor', [filePath, '--decompile'], { cwd: 'bin' });
command = Command.sidecar("bin/extractor", [filePath, "--decompile"], {
cwd: "bin",
});
}
return await command.execute();
@ -63,12 +81,18 @@ export async function extractISO(filePath) {
* @param {String} filePath
* @returns {Promise<ChildProcess>}
*/
export async function compileGame(filePath) {
export async function compileGame(filePath) {
let command;
if (isInDebugMode()) {
command = Command.sidecar('bin/extractor', [filePath, '--compile', '--proj-path', debugPath], { cwd: 'bin' })
command = Command.sidecar(
"bin/extractor",
[filePath, "--compile", "--proj-path", debugPath],
{ cwd: "bin" }
);
} else {
command = Command.sidecar('bin/extractor', [filePath, '--compile'], { cwd: 'bin' });
command = Command.sidecar("bin/extractor", [filePath, "--compile"], {
cwd: "bin",
});
}
return await command.execute();

View file

@ -1,46 +1,54 @@
const { downloadRelease } = require('@terascope/fetch-github-release');
const { app } = require('electron');
const { downloadRelease } = require("@terascope/fetch-github-release");
const { app } = require("electron");
const user = 'xTVaser';
const user = "xTVaser";
// const user = 'open-goal';
const repo = 'jak-project';
const outputdir = app.getPath('userData') + '/release';
const repo = "jak-project";
const outputdir = app.getPath("userData") + "/release";
const leaveZipped = false;
const disableLogging = false;
// Define a function to filter releases.
function filterRelease(release) {
// Filter out prereleases.
return release.prerelease === false;
// Filter out prereleases.
return release.prerelease === false;
}
// Define a function to filter assets.
function filterAsset(asset) {
// filter the assets based on the user OS
switch (process.platform) {
case 'win32':
return asset.name.includes('windows');
case 'darwin':
return asset.name.includes('mac');
case 'linux':
return asset.name.includes('linux');
default:
return asset.name.includes('windows');
}
// filter the assets based on the user OS
switch (process.platform) {
case "win32":
return asset.name.includes("windows");
case "darwin":
return asset.name.includes("mac");
case "linux":
return asset.name.includes("linux");
default:
return asset.name.includes("windows");
}
}
function fetchMasterRelease() {
downloadRelease(user, repo, outputdir, filterRelease, filterAsset, leaveZipped, disableLogging)
.then(function () {
console.log('Master release downloaded successfully!');
app.emit('console', 'Master release downloaded successfully!');
})
.catch(function (err) {
console.error(err.message);
app.emit('console', err.message);
});
downloadRelease(
user,
repo,
outputdir,
filterRelease,
filterAsset,
leaveZipped,
disableLogging
)
.then(function () {
console.log("Master release downloaded successfully!");
app.emit("console", "Master release downloaded successfully!");
})
.catch(function (err) {
console.error(err.message);
app.emit("console", err.message);
});
}
module.exports = {
fetchMasterRelease
}
fetchMasterRelease,
};

View file

@ -1,5 +1,5 @@
import { open } from '@tauri-apps/api/dialog';
import { readTextFile } from '@tauri-apps/api/fs';
import { open } from "@tauri-apps/api/dialog";
import { readTextFile } from "@tauri-apps/api/fs";
export async function fileExists(path) {
try {
@ -12,7 +12,11 @@ export async function fileExists(path) {
export async function filePrompt(title) {
// TODO - pull strings out into args
const path = await open({ multiple: false, directory: false, filters: [{ extensions: ['ISO'], name: 'Jak ISO File' }] });
const path = await open({
multiple: false,
directory: false,
filters: [{ extensions: ["ISO"], name: "Jak ISO File" }],
});
if (path) {
return path;

View file

@ -1,7 +1,7 @@
import App from './App.svelte'
import App from "./App.svelte";
const app = new App({
target: document.getElementById('app')
})
target: document.getElementById("app"),
});
export default app
export default app;

View file

@ -1,7 +1,7 @@
import { defineConfig } from 'vite'
import { svelte } from '@sveltejs/vite-plugin-svelte'
import { defineConfig } from "vite";
import { svelte } from "@sveltejs/vite-plugin-svelte";
// https://vitejs.dev/config/
export default defineConfig({
plugins: [svelte()]
})
plugins: [svelte()],
});