diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml new file mode 100644 index 0000000..34b5485 --- /dev/null +++ b/.github/workflows/ci.yaml @@ -0,0 +1,75 @@ +name: CI + +on: + push: + branches: [main] + pull_request: + branches: [main] + +env: + CARGO_TERM_COLOR: always + +jobs: + # Run cargo test + test: + name: Test Suite + runs-on: ubuntu-latest + steps: + - name: Checkout sources + uses: actions/checkout@v3 + - name: Cache + uses: actions/cache@v3 + with: + path: | + ~/.cargo/bin/ + ~/.cargo/registry/index/ + ~/.cargo/registry/cache/ + ~/.cargo/git/db/ + target/ + key: ${{ runner.os }}-cargo-test-${{ hashFiles('**/Cargo.toml') }} + - name: Install stable toolchain + uses: dtolnay/rust-toolchain@stable + - name: Install Dependencies + run: sudo apt-get update; sudo apt-get install pkg-config libx11-dev libasound2-dev libudev-dev libxcb-render0-dev libxcb-shape0-dev libxcb-xfixes0-dev + - name: Run cargo test + run: cargo test + + # Run cargo clippy -- -D warnings + clippy_check: + name: Clippy + runs-on: ubuntu-latest + steps: + - name: Checkout sources + uses: actions/checkout@v3 + - name: Cache + uses: actions/cache@v3 + with: + path: | + ~/.cargo/bin/ + ~/.cargo/registry/index/ + ~/.cargo/registry/cache/ + ~/.cargo/git/db/ + target/ + key: ${{ runner.os }}-cargo-clippy-${{ hashFiles('**/Cargo.toml') }} + - name: Install stable toolchain + uses: dtolnay/rust-toolchain@stable + with: + components: clippy + - name: Install Dependencies + run: sudo apt-get update; sudo apt-get install pkg-config libx11-dev libasound2-dev libudev-dev + - name: Run clippy + run: cargo clippy -- -D warnings + + # Run cargo fmt --all -- --check + format: + name: Format + runs-on: ubuntu-latest + steps: + - name: Checkout sources + uses: actions/checkout@v3 + - name: Install stable toolchain + uses: dtolnay/rust-toolchain@stable + with: + components: rustfmt + - name: Run cargo fmt + run: cargo fmt --all -- --check diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml new file mode 100644 index 0000000..6048504 --- /dev/null +++ b/.github/workflows/release.yaml @@ -0,0 +1,240 @@ +name: Release + +on: + push: + tags: + - '*' + +env: + # update with the name of the main binary + binary: bevy_github_ci_template + add_binaries_to_github_release: true + #itch_target: / + + +jobs: + + # Build for wasm + release-wasm: + runs-on: ubuntu-latest + + steps: + - uses: olegtarasov/get-tag@v2.1.2 + id: get_version + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@stable + with: + targets: wasm32-unknown-unknown + - name: install wasm-bindgen-cli + run: | + cargo install wasm-bindgen-cli + + - name: Build + run: | + cargo build --release --target wasm32-unknown-unknown + + - name: Prepare package + run: | + wasm-bindgen --no-typescript --out-name bevy_game --out-dir wasm --target web target/wasm32-unknown-unknown/release/${{ env.binary }}.wasm + cp -r assets wasm/ + + - name: Package as a zip + working-directory: ./wasm + run: | + zip --recurse-paths ../${{ env.binary }}.zip . + + - name: Upload binaries to artifacts + uses: actions/upload-artifact@v3 + with: + path: ${{ env.binary }}.zip + name: wasm + + - name: Upload binaries to release + if: ${{ env.add_binaries_to_github_release == 'true' }} + uses: svenstaro/upload-release-action@v2 + with: + repo_token: ${{ secrets.GITHUB_TOKEN }} + file: ${{ env.binary }}.zip + asset_name: ${{ env.binary }}-wasm-${{ steps.get_version.outputs.tag }}.zip + tag: ${{ github.ref }} + overwrite: true + + # Build for Linux + release-linux: + runs-on: ubuntu-latest + + steps: + - uses: olegtarasov/get-tag@v2.1.2 + id: get_version + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@stable + with: + targets: x86_64-unknown-linux-gnu + - name: install dependencies + run: | + sudo apt-get update; sudo apt-get install pkg-config libx11-dev libasound2-dev libudev-dev libxcb-render0-dev libxcb-shape0-dev libxcb-xfixes0-dev + + - name: Build + run: | + cargo build --release --target x86_64-unknown-linux-gnu + + - name: Prepare package + run: | + mkdir linux + cp target/x86_64-unknown-linux-gnu/release/${{ env.binary }} linux/ + cp -r assets linux/ + + - name: Package as a zip + working-directory: ./linux + run: | + zip --recurse-paths ../${{ env.binary }}.zip . + + - name: Upload binaries to artifacts + uses: actions/upload-artifact@v3 + with: + path: ${{ env.binary }}.zip + name: linux + + - name: Upload binaries to release + if: ${{ env.add_binaries_to_github_release == 'true' }} + uses: svenstaro/upload-release-action@v2 + with: + repo_token: ${{ secrets.GITHUB_TOKEN }} + file: ${{ env.binary }}.zip + asset_name: ${{ env.binary }}-linux-${{ steps.get_version.outputs.tag }}.zip + tag: ${{ github.ref }} + overwrite: true + + # Build for Windows + release-windows: + runs-on: windows-latest + + steps: + - uses: olegtarasov/get-tag@v2.1.2 + id: get_version + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@stable + with: + targets: x86_64-pc-windows-msvc + + - name: Build + run: | + cargo build --release --target x86_64-pc-windows-msvc + + - name: Prepare package + run: | + mkdir windows + cp target/x86_64-pc-windows-msvc/release/${{ env.binary }}.exe windows/ + cp -r assets windows/ + + - name: Package as a zip + run: | + Compress-Archive -Path windows/* -DestinationPath ${{ env.binary }}.zip + + - name: Upload binaries to artifacts + uses: actions/upload-artifact@v3 + with: + path: ${{ env.binary }}.zip + name: windows + + - name: Upload binaries to release + if: ${{ env.add_binaries_to_github_release == 'true' }} + uses: svenstaro/upload-release-action@v2 + with: + repo_token: ${{ secrets.GITHUB_TOKEN }} + file: ${{ env.binary }}.zip + asset_name: ${{ env.binary }}-windows-${{ steps.get_version.outputs.tag }}.zip + tag: ${{ github.ref }} + overwrite: true + + # Build for macOS + release-macos: + runs-on: macOS-latest + + steps: + - uses: olegtarasov/get-tag@v2.1.2 + id: get_version + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@stable + with: + targets: x86_64-apple-darwin + - name: Environment Setup + run: | + export CFLAGS="-fno-stack-check" + export MACOSX_DEPLOYMENT_TARGET="10.9" + + - name: Build + run: | + cargo build --release --target x86_64-apple-darwin + + - name: Prepare Package + run: | + mkdir -p ${{ env.binary }}.app/Contents/MacOS + cp target/x86_64-apple-darwin/release/${{ env.binary }} ${{ env.binary }}.app/Contents/MacOS/ + cp -r assets ${{ env.binary }}.app/Contents/MacOS/ + hdiutil create -fs HFS+ -volname "${{ env.binary }}" -srcfolder ${{ env.binary }}.app ${{ env.binary }}.dmg + + - name: Upload binaries to artifacts + uses: actions/upload-artifact@v3 + with: + path: ${{ env.binary }}.dmg + name: mac + + - name: Upload binaries to release + if: ${{ env.add_binaries_to_github_release == 'true' }} + uses: svenstaro/upload-release-action@v2 + with: + repo_token: ${{ secrets.GITHUB_TOKEN }} + file: ${{ env.binary }}.dmg + asset_name: ${{ env.binary }}-macos-${{ steps.get_version.outputs.tag }}.dmg + tag: ${{ github.ref }} + overwrite: true + + check-if-upload-to-itch-is-configured: + runs-on: ubuntu-latest + outputs: + should-upload: ${{ steps.check-env.outputs.has-itch-target }} + steps: + - id: check-env + run: | + if [[ -z "$itch_target" ]]; then + echo "has-itch-target=no" >> $GITHUB_OUTPUT + else + echo "has-itch-target=yes" >> $GITHUB_OUTPUT + fi + + upload-to-itch: + runs-on: ubuntu-latest + needs: + - check-if-upload-to-itch-is-configured + - release-wasm + - release-linux + - release-windows + - release-macos + if: ${{ needs.check-if-upload-to-itch-is-configured.outputs.should-upload == 'yes' }} + + steps: + - name: Download artifacts + uses: actions/download-artifact@v3 + with: + path: ./builds + + - name: Install butler + run: | + curl -L -o butler.zip https://broth.itch.ovh/butler/linux-amd64/LATEST/archive/default + unzip butler.zip + chmod +x butler + ./butler -V + - uses: olegtarasov/get-tag@v2.1.2 + id: get_version + - name: Upload to itch.io + env: + BUTLER_API_KEY: ${{ secrets.BUTLER_CREDENTIALS }} + run: | + for channel in $(ls builds); do + ./butler push \ + --fix-permissions \ + --userversion="${{ steps.get_version.outputs.tag }}" \ + builds/$channel/* \ + ${{ env.itch_target }}:$channel + done diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..072c7fc --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "bevy_github_ci_template" +version = "0.1.0" +edition = "2021" +license = "MIT OR Apache-2.0" + +[dependencies] +bevy = "0.10" diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..fbd8360 --- /dev/null +++ b/LICENSE @@ -0,0 +1,6 @@ +Bevy is dual-licensed under either + +* MIT License (docs/LICENSE-MIT or http://opensource.org/licenses/MIT) +* Apache License, Version 2.0 (docs/LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0) + +at your option. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..321cf7c --- /dev/null +++ b/README.md @@ -0,0 +1,78 @@ +# Bevy GitHub CI Template + +This repo show how to set up CI on a GitHub project for Bevy. + +It creates two workflows: + +* [CI](#CI) +* [Release](#Release) + +## CI + +Definition: [.github/workflows/ci.yaml](./.github/workflows/ci.yaml) + +This workflow runs on every commit to `main` branch, and on every PR targeting the `main` branch. + +It will use rust stable on linux, with cache between different executions, those commands: + +* `cargo test` +* `cargo clippy -- -D warnings` +* `cargo fmt --all -- --check` + +If you are using anything OS specific or rust nightly, you should update the file [ci.yaml](./.github/workflows/ci.yaml) to use those. + +## Release + +Definition: [.github/workflows/release.yaml](./.github/workflows/release.yaml) + +This workflow runs on every tag. + +It will build: +* For Linux and Windows, a .zip archive containing the executable and the `assets`. +* For macOS, a dmg image with a .app containing the assets. +* For wasm, a .zip archive with the wasm binary, the js bindings, an html file loading it, and the assets. + +If you don't want to target some of those platforms, you can remove the corresponding job from the file [release.yaml](./.github/workflows/release.yaml). + +If you don't want to attach the builds to the GitHub release, set `env.add_binaries_to_github_release` to `false`. + +### Git Tag from GitHub UI + +You can follow [Managing releases in a repository](https://docs.github.com/en/repositories/releasing-projects-on-github/managing-releases-in-a-repository) + +### Git Tag from the CLI + +Execute the following commands: + +```sh +git tag -a "my-game-1.0" -m "First official release" +git push --tags +``` + +### Result + +A new release will be available in GitHub, with the archives per platform available as downloadable assets. + +The `git` commands above produced this release: [my-game-1.0]( +https://github.com/bevyengine/bevy_github_ci_template/releases/tag/my-game-1.0). + +## Using the workflows in your own project + +If you would like to use the GitHub workflows included here for your own project, there are a few things you might have to adapt: + +1. The release workflow relies on the `index.html` file under `/wasm` for web builds +2. Make sure that the env variable `binary` ([release.yaml](.github/workflows/release.yaml#L10)) matches the name of your binary +3. In case your project doesn't have an `assets` folder + 1. Either create one and put a `.gitkeep` file in it to be able to push it + 2. Or remove the `cp -r assets` statements in the build jobs +4. Adapt the used toolchain if you are using nightly + +### Publish on itch.io + +The release flow can be configured to push the releases to itch.io: + +1. Create an API key in https://itch.io/user/settings/api-keys +2. Go to the repository's Settings tab in GitHub, click on Secrets->Actions in the sidebar,and add a repository secret named `BUTLER_CREDENTIALS` set to the API key. +3. Uncomment `env.itch_target` in `release.yaml` and set it to the itch.io username and the name of the game on itch.io, separated by a slash (`/`) + +Once that is done, any tag pushed to GitHub will trigger an itch.io release and use the tag as the [user version](https://itch.io/docs/butler/pushing.html#specifying-your-own-version-number). diff --git a/assets/icon.png b/assets/icon.png new file mode 100644 index 0000000..5e72def Binary files /dev/null and b/assets/icon.png differ diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..c2369c0 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,22 @@ +// Bevy code commonly triggers these lints and they may be important signals +// about code quality. They are sometimes hard to avoid though, and the CI +// workflow treats them as errors, so this allows them throughout the project. +// Feel free to delete this line. +#![allow(clippy::too_many_arguments, clippy::type_complexity)] + +use bevy::prelude::*; + +fn main() { + App::new() + .add_plugins(DefaultPlugins) + .add_startup_system(setup) + .run(); +} + +fn setup(mut commands: Commands, asset_server: Res) { + commands.spawn(Camera2dBundle::default()); + commands.spawn(SpriteBundle { + texture: asset_server.load("icon.png"), + ..Default::default() + }); +} diff --git a/wasm/index.html b/wasm/index.html new file mode 100644 index 0000000..ed8eec9 --- /dev/null +++ b/wasm/index.html @@ -0,0 +1,17 @@ + + + + + + + + diff --git a/wasm/restart-audio-context.js b/wasm/restart-audio-context.js new file mode 100644 index 0000000..69d9e4b --- /dev/null +++ b/wasm/restart-audio-context.js @@ -0,0 +1,57 @@ +// taken from https://developer.chrome.com/blog/web-audio-autoplay/#moving-forward +(function () { + // An array of all contexts to resume on the page + const audioContextList = []; + + // An array of various user interaction events we should listen for + const userInputEventNames = [ + 'click', + 'contextmenu', + 'auxclick', + 'dblclick', + 'mousedown', + 'mouseup', + 'pointerup', + 'touchend', + 'keydown', + 'keyup', + ]; + + // A proxy object to intercept AudioContexts and + // add them to the array for tracking and resuming later + self.AudioContext = new Proxy(self.AudioContext, { + construct(target, args) { + const result = new target(...args); + audioContextList.push(result); + return result; + }, + }); + + // To resume all AudioContexts being tracked + function resumeAllContexts(event) { + let count = 0; + + audioContextList.forEach(context => { + if (context.state !== 'running') { + context.resume(); + } else { + count++; + } + }); + + // If all the AudioContexts have now resumed then we + // unbind all the event listeners from the page to prevent + // unnecessary resume attempts + if (count == audioContextList.length) { + userInputEventNames.forEach(eventName => { + document.removeEventListener(eventName, resumeAllContexts); + }); + } + } + + // We bind the resume function for each user interaction + // event on the page + userInputEventNames.forEach(eventName => { + document.addEventListener(eventName, resumeAllContexts); + }); +})();