WARNING: THIS SITE IS A MIRROR OF GITHUB.COM / IT CANNOT LOGIN OR REGISTER ACCOUNTS / THE CONTENTS ARE PROVIDED AS-IS / THIS SITE ASSUMES NO RESPONSIBILITY FOR ANY DISPLAYED CONTENT OR LINKS / IF YOU FOUND SOMETHING MAY NOT GOOD FOR EVERYONE, CONTACT ADMIN AT ilovescratch@foxmail.com
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 51 additions & 13 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -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:
Expand All @@ -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/*/*
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
90 changes: 90 additions & 0 deletions cmd/init.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/*
Copyright © 2025 Ryan Flush <[email protected]>

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 <http://www.gnu.org/licenses/>.
*/
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"
`

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)
}
80 changes: 80 additions & 0 deletions cmd/init_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
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")
}

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())
}
})
}
22 changes: 21 additions & 1 deletion readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -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!