diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..855db0e --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,179 @@ +name: Release + +# On GitHub Release created (tag vX.Y.Z): validate version, push Docker +# :version + :latest, attach Linux/Windows Tauri demo installers. + +on: + release: + types: [created] + +permissions: + contents: write + packages: write + +env: + FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true + CARGO_TERM_COLOR: always + +jobs: + validate: + name: Validate Tag + runs-on: ubuntu-latest + outputs: + version: ${{ steps.extract.outputs.version }} + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + ref: ${{ github.event.release.tag_name }} + + - name: Validate release tag matches Cargo.toml + id: extract + run: | + TAG="${{ github.event.release.tag_name }}" + if [[ ! "$TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "Invalid tag format: $TAG (expected vX.Y.Z)" + exit 1 + fi + VERSION="${TAG#v}" + ROOT_VERSION="$(awk '/^\[workspace.package\]/{p=1;next} p&&/^version = /{gsub(/"/,"",$3); print $3; exit}' Cargo.toml)" + DESKTOP_VERSION="$(awk '/^version = /{gsub(/"/,"",$3); print $3; exit}' demo-desktop/src-tauri/Cargo.toml)" + TAURI_VERSION="$(python3 -c "import json; print(json.load(open('demo-desktop/src-tauri/tauri.conf.json'))['version'])")" + if [ "$VERSION" != "$ROOT_VERSION" ]; then + echo "Tag $VERSION != workspace Cargo.toml $ROOT_VERSION" + exit 1 + fi + if [ "$VERSION" != "$DESKTOP_VERSION" ]; then + echo "Tag $VERSION != demo-desktop Cargo.toml $DESKTOP_VERSION" + exit 1 + fi + if [ "$VERSION" != "$TAURI_VERSION" ]; then + echo "Tag $VERSION != tauri.conf.json $TAURI_VERSION" + exit 1 + fi + echo "version=$VERSION" >> "$GITHUB_OUTPUT" + echo "OK: tag $TAG matches $VERSION" + + docker: + name: Docker :version + :latest + needs: validate + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + ref: ${{ github.event.release.tag_name }} + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v4 + + - name: Set up Make + run: sudo apt-get install -y make + + - name: Build Docker image + run: make docker-build APP_VERSION=${{ needs.validate.outputs.version }} + + - name: Log in to Docker Hub + uses: docker/login-action@v4 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + + - name: Push :version and :latest to Docker Hub + run: make docker-push-release-hub APP_VERSION=${{ needs.validate.outputs.version }} + + - name: Log in to GHCR (personal) + uses: docker/login-action@v4 + with: + registry: ghcr.io + username: ${{ secrets.GHCR_USERNAME }} + password: ${{ secrets.GHCR_PAT }} + + - name: Push to personal GHCR + run: make docker-push-release-ghcr-personal APP_VERSION=${{ needs.validate.outputs.version }} + + - name: Log in to GHCR (Interchouette / ITC) + uses: docker/login-action@v4 + with: + registry: ghcr.io + username: ${{ secrets.GHCR_USERNAME_ITC }} + password: ${{ secrets.GHCR_PAT_ITC }} + + - name: Push to ghcr.io/interchouette and interchouette-itc + run: make docker-push-release-ghcr-itc APP_VERSION=${{ needs.validate.outputs.version }} + + - name: Redeploy Render + env: + RENDER_DEPLOY_HOOK_URL: ${{ secrets.RENDER_DEPLOY_HOOK_URL }} + run: | + if [ -z "${RENDER_DEPLOY_HOOK_URL}" ]; then + echo "RENDER_DEPLOY_HOOK_URL secret not set; skipping Render redeploy" + exit 0 + fi + hook="$(printf '%s' "$RENDER_DEPLOY_HOOK_URL" | tr -d '\r\n')" + http_code="$(curl -sS -o /tmp/render-hook.json -w '%{http_code}' --get "$hook")" + echo "render_hook_http=${http_code}" + cat /tmp/render-hook.json + echo + case "$http_code" in + 200|202) ;; + *) exit 1 ;; + esac + + desktop: + name: Tauri ${{ matrix.platform }} + needs: validate + permissions: + contents: write + strategy: + fail-fast: false + matrix: + include: + - platform: ubuntu-22.04 + - platform: windows-latest + runs-on: ${{ matrix.platform }} + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + ref: ${{ github.event.release.tag_name }} + + - name: Install Linux WebKit deps + if: matrix.platform == 'ubuntu-22.04' + run: | + sudo apt-get update + sudo apt-get install -y \ + libwebkit2gtk-4.1-dev \ + libgtk-3-dev \ + libayatana-appindicator3-dev \ + librsvg2-dev \ + patchelf \ + libfuse2 + + - name: Install Rust stable + uses: dtolnay/rust-toolchain@stable + with: + targets: wasm32-unknown-unknown + + - name: Rust cache + uses: Swatinem/rust-cache@v2 + with: + workspaces: | + demo-desktop/src-tauri -> target + demo -> target + + - name: Install trunk + run: cargo install --locked trunk --version 0.21.14 + + - name: Install tauri-cli + run: cargo install --locked tauri-cli --version '^2' + + - name: Build and upload Tauri bundles + uses: tauri-apps/tauri-action@v0 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + projectPath: demo-desktop/src-tauri + tauriScript: cargo tauri + releaseId: ${{ github.event.release.id }} + uploadUpdaterJson: false diff --git a/Makefile b/Makefile index 0eb70d9..8b6c0a1 100644 --- a/Makefile +++ b/Makefile @@ -25,7 +25,8 @@ TAG ?= dev demo demo-build demo-check \ demo-desktop demo-desktop-build \ docker-build docker-build-dev docker-run \ - docker-push-dev-hub docker-push-dev-ghcr-personal docker-push-dev-ghcr-itc + docker-push-dev-hub docker-push-dev-ghcr-personal docker-push-dev-ghcr-itc \ + docker-push-release-hub docker-push-release-ghcr-personal docker-push-release-ghcr-itc help: @echo "rangular targets" @@ -46,7 +47,7 @@ help: @echo " make demo-desktop-build Tauri release bundles" @echo "" @echo "Docker (browser SPA for Render):" - @echo " make docker-build Build $(HUB_IMAGE):$(APP_VERSION)" + @echo " make docker-build Build $(HUB_IMAGE):$(APP_VERSION) + :latest" @echo " make docker-build-dev Tag :dev + :latest (Hub + GHCR names)" @echo " make docker-run Run image on :$(DOCKER_PORT)" @echo "" @@ -141,3 +142,23 @@ docker-push-dev-ghcr-itc: docker push $(GHCR_WORKER_IMAGE):latest docker push $(GHCR_ORG_IMAGE):dev docker push $(GHCR_ORG_IMAGE):latest + +docker-push-release-hub: + docker push $(HUB_IMAGE):$(APP_VERSION) + docker push $(HUB_IMAGE):latest + +docker-push-release-ghcr-personal: + docker tag $(HUB_IMAGE):$(APP_VERSION) $(GHCR_PERSONAL_IMAGE):$(APP_VERSION) + docker tag $(HUB_IMAGE):latest $(GHCR_PERSONAL_IMAGE):latest + docker push $(GHCR_PERSONAL_IMAGE):$(APP_VERSION) + docker push $(GHCR_PERSONAL_IMAGE):latest + +docker-push-release-ghcr-itc: + docker tag $(HUB_IMAGE):$(APP_VERSION) $(GHCR_WORKER_IMAGE):$(APP_VERSION) + docker tag $(HUB_IMAGE):latest $(GHCR_WORKER_IMAGE):latest + docker tag $(HUB_IMAGE):$(APP_VERSION) $(GHCR_ORG_IMAGE):$(APP_VERSION) + docker tag $(HUB_IMAGE):latest $(GHCR_ORG_IMAGE):latest + docker push $(GHCR_WORKER_IMAGE):$(APP_VERSION) + docker push $(GHCR_WORKER_IMAGE):latest + docker push $(GHCR_ORG_IMAGE):$(APP_VERSION) + docker push $(GHCR_ORG_IMAGE):latest diff --git a/README.md b/README.md index 25e1e2a..eed4d23 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,8 @@ render with [Leptos](https://leptos.dev/) in the browser (CSR / Trunk / wasm). **Live:** [https://rangular.interchouette.net](https://rangular.interchouette.net) +Pre-built desktop demos: **[GitHub Releases](https://github.com/Interchouette-ITC/rangular/releases)** (Linux `.deb` / AppImage, Windows NSIS). macOS not in v1. + If you know Angular component files, you will feel at home. If you know Rust, you keep ownership of the real logic. **[rangular](https://github.com/Interchouette-ITC/rangular)** sits between the two: a **versioned subset**, not a full Angular port, with @@ -246,7 +248,11 @@ Leptos + Trunk build that uses rangular components can run inside that webview the same way it runs in Chrome. rangular still speaks DOM / wasm, not egui / iced / GTK. -In-repo shell (same SPA as the browser demo): +Pre-built installers (Linux `.deb` / AppImage, Windows NSIS) ship on +**[GitHub Releases](https://github.com/Interchouette-ITC/rangular/releases)**. +macOS is not in v1. + +Local shell (same SPA as the browser demo): ```bash make demo-desktop diff --git a/demo-desktop/README.md b/demo-desktop/README.md index 52df5c0..cd4592e 100644 --- a/demo-desktop/README.md +++ b/demo-desktop/README.md @@ -6,7 +6,19 @@ demo only. Stack: **Tauri → Leptos → rangular**. -## Run +## Release installers + +Pre-built bundles ship on **[GitHub Releases](https://github.com/Interchouette-ITC/rangular/releases)**: + +- Linux: `.deb` and AppImage +- Windows: NSIS `.exe` +- macOS: not in v1 + +Download from the latest release assets, then install or run the AppImage / setup +exe. The browser demo stays at +[https://rangular.interchouette.net](https://rangular.interchouette.net). + +## Run locally ```bash make demo-desktop @@ -15,7 +27,7 @@ make demo-desktop Opens a native window pointed at `http://127.0.0.1:4180/` (Trunk). If that port is already serving the browser demo, the shell reuses it. -Release bundles (Linux `deb` / AppImage, Windows NSIS): +Local release build: ```bash make demo-desktop-build diff --git a/demo/README.md b/demo/README.md index bd80251..8d3abff 100644 --- a/demo/README.md +++ b/demo/README.md @@ -61,6 +61,8 @@ make docker-run Production URL: [https://rangular.interchouette.net](https://rangular.interchouette.net) +Desktop installers: **[GitHub Releases](https://github.com/Interchouette-ITC/rangular/releases)** (Linux `.deb` / AppImage, Windows NSIS). + Pull: ```bash diff --git a/docker/DOCKERHUB.md b/docker/DOCKERHUB.md index 4279078..69b9ad0 100644 --- a/docker/DOCKERHUB.md +++ b/docker/DOCKERHUB.md @@ -17,7 +17,7 @@ Live: [https://rangular.interchouette.net](https://rangular.interchouette.net) | Tag | Meaning | | --------- | ------------------------------------------------ | | `:dev` | Tip of `dev` | -| `:latest` | Rolling tip (same digest as `:dev` on tip pushes) | +| `:latest` | Rolling tip on tip pushes; also on GitHub Release | | `:X.Y.Z` | Stable GitHub Release matching workspace version | ```bash