From 2e49a987462c5157e3ef0f59387c4455d7a7752e Mon Sep 17 00:00:00 2001 From: Flushwhy Date: Mon, 21 Jul 2025 13:46:43 -0400 Subject: [PATCH 1/3] Add new init command --- CHANGELOG.md | 1 + cmd/init.go | 91 ++++++++++++++++++++++++++++++++++++++++++++++++ cmd/init_test.go | 81 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 173 insertions(+) create mode 100644 cmd/init.go create mode 100644 cmd/init_test.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 3420954..c26db3d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/en/2.0.0/). ### Added Viper support for .fe.yaml files. New Pack command that packs muliple PNGs into one PNG. +New init command ### Changed How Transcode works, and changed commands to it. diff --git a/cmd/init.go b/cmd/init.go new file mode 100644 index 0000000..318de2e --- /dev/null +++ b/cmd/init.go @@ -0,0 +1,91 @@ +/* +Copyright Β© 2025 Ryan Flush + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ +package cmd + +import ( + "fmt" + "os" + "path/filepath" + + "github.com/spf13/cobra" +) + +const defaultConfigContent = ` +# Settings for the 'bmp' (butler) command +itchio: + username: "your-itch-username" + game: "your-itch-game-name" + +# Settings for the 'pack' command +pack: + input: "./assets/sprites" + output: "./assets/spritesheet.png" + +# Default settings for the 'transcode' command +transcode: + codec: "libvorbis" + bitrate: "128k" +` + +// initCmd represents the init command +var initCmd = &cobra.Command{ + Use: "init", + Short: "This builds/inits a game project with a standard structure.", + Long: `This creates a file structure for a game project. That follows the standard fill structure.`, + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + projectName := args[0] + + if _, err := os.Stat(projectName); !os.IsNotExist(err) { + return fmt.Errorf("directory %s already exists", projectName) + } + + fmt.Printf("πŸš€ Initializing new project: %s\n", projectName) + + dirsToCreate := []string{ + "assets/audio", + "assets/fonts", + "assets/sprites", + "builds", + "src", + } + + for _, dir := range dirsToCreate { + fullPath := filepath.Join(projectName, dir) + + if err := os.MkdirAll(fullPath, os.ModePerm); err != nil { + return fmt.Errorf("failed to create directory %s: %w", fullPath, err) + } + fmt.Printf(" βœ“ Created directory: %s\n", fullPath) + } + + configPath := filepath.Join(projectName, ".fe.yaml") + + if err := os.WriteFile(configPath, []byte(defaultConfigContent), 0644); err != nil { + return fmt.Errorf("failed to write config file: %w", err) + } + fmt.Printf(" βœ“ created config file: %s\n", configPath) + + fmt.Printf("\nπŸŽ‰ project '%s' initialization complete!", projectName) + return nil + + }, +} + +func init() { + rootCmd.AddCommand(initCmd) +} diff --git a/cmd/init_test.go b/cmd/init_test.go new file mode 100644 index 0000000..1f9fed0 --- /dev/null +++ b/cmd/init_test.go @@ -0,0 +1,81 @@ +package cmd + +import ( + "os" + "path/filepath" + "strings" + "testing" +) + +func TestInitCommand(t *testing.T) { + t.Run("successfully initializes a new project", func(t *testing.T) { + tempDir := t.TempDir() + projectName := "my-new-game" + projectPath := filepath.Join(tempDir, projectName) + + originalWd, _ := os.Getwd() + os.Chdir(tempDir) + defer os.Chdir(originalWd) + + err := initCmd.RunE(initCmd, []string{projectName}) + + if err != nil { + t.Fatalf("initCmd.RunE() returned an unexpected error: %v", err) + } + + expectedDirs := []string{ + "assets/audio", + "assets/fonts", + "assets/sprites", + "builds", + "src", + } + + for _, dir := range expectedDirs { + dirPath := filepath.Join(projectPath, dir) + if _, err := os.Stat(dirPath); os.IsNotExist(err) { + t.Errorf("expected directory to exist, but it doesn't: %s", dirPath) + } + } + + configPath := filepath.Join(projectPath, ".fe.yaml") + if _, err := os.Stat(configPath); os.IsNotExist(err) { + t.Fatalf("expected config file to exist, but it doesn't: %s", configPath) + } + + content, err := os.ReadFile(configPath) + if err != nil { + t.Fatalf("failed to read config file: %v", err) + } + + if strings.TrimSpace(string(content)) != strings.TrimSpace(defaultConfigContent) { + t.Errorf("config file content mismatch") + } + }) + + t.Run("fails if project directory already exists", func(t *testing.T) { + tempDir := t.TempDir() + projectName := "existing-project" + projectPath := filepath.Join(tempDir, projectName) + + if err := os.Mkdir(projectPath, 0755); err != nil { + t.Fatalf("failed to create pre-existing directory for test: %v", err) + } + + originalWd, _ := os.Getwd() + os.Chdir(tempDir) + defer os.Chdir(originalWd) + + err := initCmd.RunE(initCmd, []string{projectName}) + + if err == nil { + t.Fatal("expected an error when directory exists, but got nil") + } + + // β˜… FIX: Removed the single quotes to match the actual error output. + expectedErrorMsg := "directory existing-project already exists" + if !strings.Contains(err.Error(), expectedErrorMsg) { + t.Errorf("expected error message to contain '%s', but got: '%s'", expectedErrorMsg, err.Error()) + } + }) +} From a0e9731a13f8ee7f4e2d787fc80f6da6b46a63e2 Mon Sep 17 00:00:00 2001 From: Flushwhy Date: Mon, 21 Jul 2025 14:24:04 -0400 Subject: [PATCH 2/3] Update readme and removed some comments --- cmd/init.go | 1 - cmd/init_test.go | 1 - readme.md | 22 +++++++++++++++++++++- 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/cmd/init.go b/cmd/init.go index 318de2e..e490294 100644 --- a/cmd/init.go +++ b/cmd/init.go @@ -41,7 +41,6 @@ transcode: bitrate: "128k" ` -// initCmd represents the init command var initCmd = &cobra.Command{ Use: "init", Short: "This builds/inits a game project with a standard structure.", diff --git a/cmd/init_test.go b/cmd/init_test.go index 1f9fed0..f71ccfe 100644 --- a/cmd/init_test.go +++ b/cmd/init_test.go @@ -72,7 +72,6 @@ func TestInitCommand(t *testing.T) { t.Fatal("expected an error when directory exists, but got nil") } - // β˜… FIX: Removed the single quotes to match the actual error output. expectedErrorMsg := "directory existing-project already exists" if !strings.Contains(err.Error(), expectedErrorMsg) { t.Errorf("expected error message to contain '%s', but got: '%s'", expectedErrorMsg, err.Error()) diff --git a/readme.md b/readme.md index 6677432..5f4abdc 100644 --- a/readme.md +++ b/readme.md @@ -137,12 +137,32 @@ fe bmp --- +### Init Game Project + +```bash +fe init MyNewGame +``` +This build a file sturcture like: +``` +MyNewGame/ +β”œβ”€β”€ .fe.yaml # Default configuration for fe +β”œβ”€β”€ assets/ +β”‚ β”œβ”€β”€ audio/ # For raw audio files (.wav, .mp3) +β”‚ β”œβ”€β”€ fonts/ # For font files (.ttf, .otf) +β”‚ └── sprites/ # For individual sprite images (.png) +β”œβ”€β”€ builds/ # For your final, compiled game executables +└── src/ # For your game's source code + +``` +--- + ## πŸ—ΊοΈ Roadmap - [x] Add audio/video transcoding - [x] Add bulk pusher for itch.io’s Butler - [x] Add texture packer (sprite sheet generator) - [ ] Add vector (SVG) to TTF font converter -- [ ] Add project scaffolding (`fe init`) +- [x] Add project scaffolding (`fe init`) +- [ ] Add frameworks for things like Mesonbuild and raylib and so on to init. - [ ] Add file watcher for auto asset processing (`fe watch`) - [ ] More awesome stuff! From 5949d009983fee79b978cc8ed0f0a2d814af107f Mon Sep 17 00:00:00 2001 From: Flushwhy Date: Mon, 21 Jul 2025 14:30:45 -0400 Subject: [PATCH 3/3] Updated the release.yml --- .github/workflows/release.yml | 64 ++++++++++++++++++++++++++++------- 1 file changed, 51 insertions(+), 13 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 729faea..a1201bf 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,11 +1,14 @@ -name: Release Build +name: Release Build and Upload +# This workflow runs when a new release is published on GitHub. on: release: types: [published] jobs: + # The 'build' job compiles the application for different platforms. build: + name: Build for ${{ matrix.goos }}-${{ matrix.goarch }} runs-on: ubuntu-latest strategy: matrix: @@ -18,28 +21,63 @@ jobs: goarch: arm64 - goos: linux goarch: amd64 + steps: - - uses: actions/checkout@v4 + - name: Checkout code + uses: actions/checkout@v4 + - name: Set up Go uses: actions/setup-go@v5 with: go-version: '1.23.2' + + - name: Set output binary name and extension + id: vars + run: | + # Set the base name for the binary. + binary_name="fe" + # Add .exe extension for Windows builds. + if [ "${{ matrix.goos }}" = "windows" ]; then + echo "binary_name_ext=${binary_name}.exe" >> $GITHUB_OUTPUT + echo "archive_name=${binary_name}-${{ matrix.goos }}-${{ matrix.goarch }}.zip" >> $GITHUB_OUTPUT + else + echo "binary_name_ext=${binary_name}" >> $GITHUB_OUTPUT + echo "archive_name=${binary_name}-${{ matrix.goos }}-${{ matrix.goarch }}.tar.gz" >> $GITHUB_OUTPUT + fi + - name: Build binary run: | - mkdir -p build - GOOS=${{ matrix.goos }} GOARCH=${{ matrix.goarch }} go build -o build/pngjoiner-${{ matrix.goos }}-${{ matrix.goarch }} ./cmd - - name: Upload artifact + GOOS=${{ matrix.goos }} GOARCH=${{ matrix.goarch }} go build -ldflags="-s -w" -o "build/${{ steps.vars.outputs.binary_name_ext }}" . + + - name: Create release archive + run: | + cd build + if [ "${{ matrix.goos }}" = "windows" ]; then + zip ../${{ steps.vars.outputs.archive_name }} ${{ steps.vars.outputs.binary_name_ext }} + else + tar -czvf ../${{ steps.vars.outputs.archive_name }} ${{ steps.vars.outputs.binary_name_ext }} + fi + cd .. + + - name: Upload artifact for release job uses: actions/upload-artifact@v4 with: - name: pngjoiner-${{ matrix.goos }}-${{ matrix.goarch }} - path: build/pngjoiner-${{ matrix.goos }}-${{ matrix.goarch }} + name: ${{ steps.vars.outputs.archive_name }} + path: ${{ steps.vars.outputs.archive_name }} - flatpak: + release: + name: Upload Release Assets runs-on: ubuntu-latest + needs: build + steps: - - uses: actions/checkout@v4 - - name: Install flatpak tools - run: sudo apt-get update && sudo apt-get install -y flatpak flatpak-builder - - name: Build Flatpak (placeholder) - run: echo "You need to add a flatpak manifest and build instructions here." + - name: Download all release artifacts + uses: actions/download-artifact@v4 + with: + path: release-artifacts + + - name: Upload assets to GitHub Release + uses: softprops/action-gh-release@v2 + with: + files: release-artifacts/*/*