diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index f20142cf8..090e60c22 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -1222,8 +1222,8 @@ jobs: } continue-on-error: true - - name: Scan media/ via GitHub Action - uses: ./.github/actions/qtmesh + - name: Scan media/ via GitHub Action (root action.yml) + uses: ./ with: command: scan input-file: . diff --git a/CLAUDE.md b/CLAUDE.md index 305f3f788..994bbdd59 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -259,6 +259,34 @@ The `winget-publish` CI job uses `wingetcreate --submit` to automatically submit Manual alternative: `./scripts/update-winget.sh ` generates the manifest locally. +## GitHub Action (Marketplace) + +The `qtmesh` CLI is published as a GitHub Action on the [GitHub Actions Marketplace](https://github.com/marketplace/actions/qtmesh). The `action.yml` lives at the repo root. + +```yaml +- uses: fernandotonon/QtMeshEditor@v1 + with: + command: scan + input-file: ./assets + options: --fail-on warning +``` + +**Key files:** +- `action.yml` — root-level action definition (required for marketplace) +- `.github/actions/qtmesh/action.yml` — legacy local action (kept for backward compatibility) +- Docker image: `ghcr.io/fernandotonon/qtmesh` (built from this repo on each release) +- Redirect repo: `fernandotonon/qtmesh` points users to this repo + +**When to update action.yml:** +- New CLI subcommand added → update `command` description +- Subcommand flags change → update `options` description +- Docker image name/registry changes → update the `docker run` command +- **No update needed** for: bug fixes, GUI changes, MCP tools, or features that don't change CLI interface + +The action uses `image-tag: latest` by default, so users automatically get fixes without version bumps. + +**Marketplace publishing:** When creating a GitHub Release, check "Publish this Action to the GitHub Marketplace". The `v1` tag should be kept pointing to the latest stable commit (force-push tag on each release). + ## CI/CD -GitHub Actions workflow in `.github/workflows/deploy.yml` builds for Windows (MinGW), macOS, and Linux. Tests run on Linux with SonarCloud coverage. Releases auto-update the Homebrew cask, WinGet package, Snap Store, and Docker image. +GitHub Actions workflow in `.github/workflows/deploy.yml` builds for Windows (MinGW), macOS, and Linux. Tests run on Linux with SonarCloud coverage. Releases auto-update the Homebrew cask, WinGet package, Snap Store, and Docker image. A `scan-assets-docker` job runs the `fernandotonon/qtmesh` action on the repo's own test assets to validate the Docker image and scan pipeline on every push/PR. diff --git a/action.yml b/action.yml new file mode 100644 index 000000000..c885439f8 --- /dev/null +++ b/action.yml @@ -0,0 +1,74 @@ +name: 'qtmesh' +description: 'Validate, convert, and optimize 3D assets in CI. Scan for issues, convert 40+ formats, and more.' +author: 'Fernando Tonon' + +branding: + icon: 'box' + color: 'blue' + +inputs: + command: + description: 'Subcommand: scan, info, validate, convert, fix, anim, lod, pose' + required: true + input-file: + description: 'Input file or directory path (relative to workspace)' + required: true + output-file: + description: 'Output file path (for convert/fix/anim/pose)' + required: false + options: + description: 'Additional CLI flags (e.g. --json, --fail-on warning, --resample 30)' + required: false + image-tag: + description: 'Docker image tag (default: latest)' + required: false + default: 'latest' + +outputs: + result: + description: 'Command stdout (text or JSON depending on --json flag)' + value: ${{ steps.run.outputs.result }} + exit-code: + description: 'Exit code (0 = success, 1 = issues found for scan/validate)' + value: ${{ steps.run.outputs.exit-code }} + +runs: + using: 'composite' + steps: + - id: run + shell: bash + env: + INPUT_COMMAND: ${{ inputs.command }} + INPUT_FILE: ${{ inputs.input-file }} + INPUT_OUTPUT_FILE: ${{ inputs.output-file }} + INPUT_OPTIONS: ${{ inputs.options }} + run: | + # Build command args safely via arrays to prevent injection + cmd=("$INPUT_COMMAND" "/workspace/$INPUT_FILE") + if [ -n "$INPUT_OUTPUT_FILE" ]; then + cmd+=("-o" "/workspace/$INPUT_OUTPUT_FILE") + fi + if [ -n "$INPUT_OPTIONS" ]; then + read -r -a opts <<< "$INPUT_OPTIONS" + cmd+=("${opts[@]}") + fi + + set +e + OUTPUT=$(docker run --rm \ + --user "$(id -u):$(id -g)" \ + -v "${{ github.workspace }}:/workspace" \ + "ghcr.io/fernandotonon/qtmesh:${{ inputs.image-tag }}" \ + "${cmd[@]}" 2>&1) + EXIT_CODE=$? + set -e + + echo "result<> "$GITHUB_OUTPUT" + echo "$OUTPUT" >> "$GITHUB_OUTPUT" + echo "EOF" >> "$GITHUB_OUTPUT" + echo "exit-code=$EXIT_CODE" >> "$GITHUB_OUTPUT" + + # Print output for workflow logs + echo "$OUTPUT" + + # Propagate exit code for scan/validate (exit 1 = issues found) + exit $EXIT_CODE diff --git a/website/src/DocsApp.jsx b/website/src/DocsApp.jsx index 29d6c2c4a..8fcfdc715 100644 --- a/website/src/DocsApp.jsx +++ b/website/src/DocsApp.jsx @@ -357,7 +357,7 @@ qtmesh pose --animation --count N -o `} ]} /> - Recursively scan a directory for 3D asset issues. Think of it as ESLint for 3D assets. Checks format restrictions, complexity limits, naming conventions, skeleton/animation content, and more. Supports YAML configuration, scoped rules per folder, JSON/SARIF output, and auto-fix.} + Recursively scan a directory for 3D asset issues. Think of it as ESLint for 3D assets. Checks format restrictions, complexity limits, naming conventions, skeleton/animation content, and more. Supports YAML configuration, scoped rules per folder, JSON output, and auto-fix. Also available as a GitHub Action: fernandotonon/QtMeshEditor@v1.} synopsis={`qtmesh scan [path] [options]`} options={[ ['--config ', 'Config file path (default: qtmesh.yml, qtmesh.yaml, qtmesh.json)'], @@ -373,8 +373,7 @@ qtmesh pose --animation --count N -o `} examples={[ 'qtmesh scan ./assets', 'qtmesh scan ./assets --config qtmesh.yml --fail-on warning', - 'qtmesh scan ./assets --json --report .qtmesh/report.json', - 'qtmesh scan ./assets --sarif report.sarif', + 'qtmesh scan ./assets --json --report report.json', 'qtmesh scan ./assets --fix --dry-run', 'qtmesh scan ./assets --include "*.fbx,*.glb" --exclude "**/vendor/**"', ]} @@ -683,14 +682,55 @@ docker run --rm -v $(pwd):/workspace ghcr.io/fernandotonon/qtmesh \\

GitHub Actions

-

Reusable Action (scan example)

- {`- uses: fernandotonon/QtMeshEditor/.github/actions/qtmesh@9cfc829e8b255994ef92ba228c687e3dd2254119 +

+ The qtmesh action is available on the GitHub Actions Marketplace. Add it to any workflow with one line. +

+ +

Scan assets on every PR

+ {`name: Validate 3D Assets +on: [pull_request] + +jobs: + scan: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: fernandotonon/QtMeshEditor@v1 + with: + command: scan + input-file: ./assets + options: --fail-on warning`} + +

Convert and validate

+ {`- uses: fernandotonon/QtMeshEditor@v1 + with: + command: validate + input-file: ./models/character.fbx + +- uses: fernandotonon/QtMeshEditor@v1 + with: + command: convert + input-file: ./models/character.fbx + output-file: ./output/character.glb2 + +- uses: fernandotonon/QtMeshEditor@v1 with: - command: scan - input-file: assets - options: --config qtmesh.yml --sarif scan-report.sarif --report scan-report.json --fail-on error`} + command: anim + input-file: ./animations/dance.fbx + output-file: ./output/dance_optimized.fbx + options: --resample 30`}
+ +

Get mesh info as JSON

+ {`- uses: fernandotonon/QtMeshEditor@v1 + id: info + with: + command: info + input-file: ./models/character.fbx + options: --json + +- run: echo "\${{ steps.info.outputs.result }}"`} -

Direct Docker Usage

+

Direct Docker usage

{`- name: Scan assets run: | docker run --rm \\ @@ -698,14 +738,7 @@ docker run --rm -v $(pwd):/workspace ghcr.io/fernandotonon/qtmesh \\ ghcr.io/fernandotonon/qtmesh:latest \\ scan /workspace/assets \\ --config /workspace/qtmesh.yml \\ - --sarif /workspace/scan-report.sarif \\ - --fail-on error - -- name: Upload SARIF to GitHub - if: always() - uses: github/codeql-action/upload-sarif@v3 - with: - sarif_file: scan-report.sarif`} + --fail-on error`}
diff --git a/website/src/data/content.js b/website/src/data/content.js index a77074150..f06a19e24 100644 --- a/website/src/data/content.js +++ b/website/src/data/content.js @@ -6,7 +6,8 @@ export const links = { issues: 'https://github.com/fernandotonon/QtMeshEditor/issues', forum: 'https://forums.ogre3d.org/viewtopic.php?t=76016', license: 'https://opensource.org/license/mit', - actions: 'https://github.com/fernandotonon/QtMeshEditor/tree/master/.github/actions/qtmesh' + actions: 'https://github.com/fernandotonon/qtmesh', + marketplace: 'https://github.com/marketplace/actions/qtmesh' }; export const media = { @@ -76,18 +77,18 @@ export const useCases = [ body: 'Combine Mixamo, Unreal exports, and DCC clips into clean output files for engine ingestion.' }, { - title: 'Run in CI/CD', - body: 'Use the same commands in local scripts, Docker containers, and GitHub Actions workflows.' + title: 'GitHub Actions (Marketplace)', + body: 'Add `fernandotonon/QtMeshEditor@v1` to any workflow. Scan assets on every PR, convert formats, validate meshes — one line in your YAML.' } ]; export const pipelineExamples = { - scan: `# Preview workflow (actively evolving)\nqtmesh scan ./assets --fail-on error\nqtmesh scan ./assets --sarif reports/qtmesh.sarif --report reports/qtmesh.json`, + scan: `qtmesh scan ./assets --fail-on error\nqtmesh scan ./assets --json --report reports/qtmesh.json`, fix: `qtmesh fix model.fbx -o fixed.fbx\nqtmesh fix model.fbx --all -o fixed.fbx`, convert: `qtmesh convert model.fbx -o model.glb2\nqtmesh convert model.dae -o model.mesh`, merge: `qtmesh anim base.fbx \\\n --merge walk.fbx run.fbx jump.fbx idle.fbx \\\n -o merged.fbx`, docker: `docker run --rm --user "$(id -u):$(id -g)" -v $(pwd):/workspace \\\n ghcr.io/fernandotonon/qtmesh scan ./assets --fail-on error`, - githubAction: `- uses: fernandotonon/QtMeshEditor/.github/actions/qtmesh@c56db1ce3f2b29960cc1c9f82f5be98a51594138\n with:\n command: scan\n input-file: assets\n options: --fail-on error`, + githubAction: `# GitHub Actions Marketplace: fernandotonon/qtmesh\n- uses: fernandotonon/QtMeshEditor@v1\n with:\n command: scan\n input-file: ./assets\n options: --fail-on warning`, scanFixConvert: `qtmesh scan ./assets --fail-on error\nqtmesh fix character.fbx --all -o character_fixed.fbx\nqtmesh convert character_fixed.fbx -o character.glb2` };