Compare commits

..
Author SHA1 Message Date
bflatastic 00696060c4 artcraft-native: generate with Midjourney or fal (provider choice)
Images can run on Midjourney with the user's account or on fal with their
API key, picked from the model selector's "Select Provider" submenu.
Midjourney connects through a sign-in window (system webview), fal through
Settings. Jobs are tracked locally and their results uploaded into the
library, resuming after a restart.

The Midjourney credential manager and the router prompt helper move into
midjourney_client and artcraft_router, shared by both desktop apps.
2026-10-11 20:46:39 +07:00
24 changed files with 34 additions and 642 deletions
-6
View File
@@ -1,6 +0,0 @@
# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json
reviews:
auto_review:
# Dependabot bumps are checked by CI; skip CodeRabbit reviews on them.
ignore_usernames:
- "dependabot[bot]"
+8 -27
View File
@@ -1,30 +1,11 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
version: 2
updates:
# Maintain Rust / Cargo dependencies
- package-ecosystem: "cargo"
directory: "/"
- package-ecosystem: "cargo" # See documentation for possible values
directory: "/" # Location of package manifests
schedule:
interval: "daily"
time: "06:00"
timezone: "America/New_York"
# Consolidate updates into a single PR rather than one PR per crate
groups:
cargo-dependencies:
patterns:
- "*"
commit-message:
prefix: "chore(deps)"
# Maintain GitHub Actions workflows
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "daily"
time: "06:00"
timezone: "America/New_York"
groups:
actions:
patterns:
- "*"
commit-message:
prefix: "ci(deps)"
interval: "weekly"
@@ -1,193 +0,0 @@
name: ArtCraft Linux release
on:
push:
branches: [release, artcraft-release, artcraft-linux]
workflow_dispatch:
permissions:
contents: read
concurrency:
group: linux-release-${{ github.ref }}
cancel-in-progress: false
jobs:
packaging-check:
runs-on: ubuntu-22.04
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- uses: storytold/craft-common-actions/actions/install-apt-packages@main
with:
packages: rpm jq binutils zsync
- run: packaging/linux/package-test.sh
build:
strategy:
fail-fast: false
matrix:
include:
- {arch: x86_64, runner: ubuntu-22.04, target: x86_64-unknown-linux-gnu, bundles: "deb,rpm,appimage"}
- {arch: aarch64, runner: ubuntu-22.04-arm, target: aarch64-unknown-linux-gnu, bundles: "deb,rpm,appimage"}
- {arch: riscv64, runner: ubuntu-24.04, target: riscv64gc-unknown-linux-gnu, bundles: "deb,rpm"}
runs-on: ${{ matrix.runner }}
timeout-minutes: 90
env:
SQLX_OFFLINE: 'true'
VITE_ENVIRONMENT_TYPE: production
TAURI_FRONTEND_PATH: ${{ github.workspace }}/frontend
TAURI_APP_PATH: ${{ github.workspace }}/crates/desktop/artcraft
steps:
- uses: actions/checkout@v4
- uses: storytold/craft-common-actions/actions/install-apt-packages@main
with:
packages: libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf libssl-dev libclang-dev libasound2-dev libxdo-dev libdbus-1-dev build-essential clang file jq perl cmake nasm rpm zsync gstreamer1.0-plugins-base gstreamer1.0-plugins-good gstreamer1.0-libav
- uses: storytold/craft-common-actions/actions/install-riscv64-cross@main
if: matrix.arch == 'riscv64'
- name: Remove conflicting host Pango development files
if: matrix.arch == 'riscv64'
# Ubuntu's Pango GIR differs by architecture despite being in a shared path.
# Only the target GTK development packages are needed for this cross build.
run: sudo apt-get remove -y libpango1.0-dev:amd64
- name: Install RISC-V WebKitGTK dependencies
if: matrix.arch == 'riscv64'
uses: storytold/craft-common-actions/actions/install-apt-packages@main
with:
packages: libwebkit2gtk-4.1-dev:riscv64 libappindicator3-dev:riscv64 libssl-dev:riscv64 libxdo-dev:riscv64 libdbus-1-dev:riscv64 g++-riscv64-linux-gnu
- name: Configure RISC-V cross compilation
if: matrix.arch == 'riscv64'
run: |
cat >> "$GITHUB_ENV" <<'ENV'
CARGO_TARGET_RISCV64GC_UNKNOWN_LINUX_GNU_LINKER=riscv64-linux-gnu-gcc
CC_riscv64gc_unknown_linux_gnu=riscv64-linux-gnu-gcc
CXX_riscv64gc_unknown_linux_gnu=riscv64-linux-gnu-g++
AR_riscv64gc_unknown_linux_gnu=riscv64-linux-gnu-ar
PKG_CONFIG_ALLOW_CROSS=1
PKG_CONFIG_PATH_riscv64gc_unknown_linux_gnu=/usr/lib/riscv64-linux-gnu/pkgconfig:/usr/share/pkgconfig
PKG_CONFIG_LIBDIR_riscv64gc_unknown_linux_gnu=/usr/lib/riscv64-linux-gnu/pkgconfig:/usr/share/pkgconfig
ENV
echo "CMAKE_TOOLCHAIN_FILE_riscv64gc_unknown_linux_gnu=$GITHUB_WORKSPACE/packaging/linux/riscv64.cmake" >> "$GITHUB_ENV"
- uses: actions/setup-node@v4
with:
node-version: '22'
cache: npm
cache-dependency-path: frontend/package-lock.json
- uses: dtolnay/rust-toolchain@683b588c487a69d01f6c6ddf40f036ff682cc2c8 # v1
with:
toolchain: stable
targets: ${{ matrix.target }}
- uses: Swatinem/rust-cache@d1527c8f67ef9af357290475d4af4b0fab8f12e0 # v2.9.3
with:
key: tauri-linux-${{ matrix.arch }}
cache-on-failure: true
- run: npm ci --ignore-scripts
working-directory: frontend
- run: ./node_modules/.bin/nx run artcraft:build
working-directory: frontend
- uses: tauri-apps/tauri-action@fce9c6108b31ea247710505d3aaaa893ee6768d4 # v0
with:
projectPath: crates/desktop/artcraft
args: --target ${{ matrix.target }} --bundles ${{ matrix.bundles }} --config ${{ github.workspace }}/packaging/linux/tauri.conf.json
- name: Package and verify Linux outputs
env:
ARCH: ${{ matrix.arch }}
TARGET: ${{ matrix.target }}
run: packaging/linux/package.sh "$ARCH" "$TARGET"
- uses: actions/upload-artifact@v4
with:
name: release-linux-${{ matrix.arch }}
path: dist/release/*
if-no-files-found: error
arch:
if: always() && !cancelled()
needs: build
runs-on: ubuntu-24.04
strategy:
matrix:
arch: [x86_64, aarch64]
steps:
- uses: actions/checkout@v4
- uses: actions/download-artifact@v4
with:
name: release-linux-${{ matrix.arch }}
path: dist/linux
- name: Find tarball
id: tarball
env:
ARCH: ${{ matrix.arch }}
run: |
shopt -s nullglob
files=(dist/linux/artcraft-*-linux-"$ARCH".tar.gz)
test "${#files[@]}" -eq 1
echo "path=${files[0]}" >> "$GITHUB_OUTPUT"
- uses: storytold/craft-common-actions/actions/arch-package@9d6790582b4997aa2120f4413cdb55c0b6f77fae # extra-depends support (#161)
with:
tarball: ${{ steps.tarball.outputs.path }}
arch: ${{ matrix.arch }}
extra-depends: webkit2gtk-4.1 gtk3 openssl
- name: Verify Arch runtime dependencies
run: |
for package in dist/release/*.pkg.tar.zst; do
tar --zstd -xOf "$package" .PKGINFO | grep -Fx 'depend = webkit2gtk-4.1'
done
- uses: actions/upload-artifact@v4
with:
name: release-arch-${{ matrix.arch }}
path: dist/release/*.pkg.tar.zst
if-no-files-found: error
flatpak:
if: always() && !cancelled()
needs: build
runs-on: ${{ matrix.runner }}
permissions:
contents: read
packages: write
strategy:
fail-fast: false
matrix:
include:
- {arch: x86_64, runner: ubuntu-24.04}
- {arch: aarch64, runner: ubuntu-24.04-arm}
steps:
- uses: actions/checkout@v4
- uses: actions/download-artifact@v4
with:
name: release-linux-${{ matrix.arch }}
path: dist/linux
- uses: storytold/craft-common-actions/actions/install-apt-packages@main
with:
packages: flatpak flatpak-builder elfutils librsvg2-common skopeo
- name: Allow Flatpak build sandboxes
run: sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0
- uses: storytold/craft-common-actions/actions/import-signing-key@main
with:
key: ${{ secrets.LINUX_SIGNING_KEY }}
- name: Build Flatpak and publish OCI
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: packaging/linux/flatpak.sh
- uses: actions/upload-artifact@v4
with:
name: release-flatpak-${{ matrix.arch }}
path: |
dist/release/*.flatpak
dist/release/*.oci-manifest.json
if-no-files-found: error
release:
needs: [packaging-check, build, arch, flatpak]
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v4
- name: Read version
id: version
run: echo "value=$(jq -r .version crates/desktop/artcraft/tauri.conf.json)" >> "$GITHUB_OUTPUT"
- name: Require signing key
env:
SIGNING_KEY: ${{ secrets.LINUX_SIGNING_KEY }}
run: test -n "$SIGNING_KEY"
- uses: storytold/craft-common-actions/actions/draft-release@main
with:
version: ${{ steps.version.outputs.value }}
app-title: ArtCraft
tag-prefix: artcraft-v
retain: "true"
github-token: ${{ secrets.GITHUB_TOKEN }}
signing-key: ${{ secrets.LINUX_SIGNING_KEY }}
+8 -10
View File
@@ -84,7 +84,7 @@ jobs:
echo "Starting certificate import process..."
# Create certificate file
echo "$APPLE_CERTIFICATE" | base64 --decode > certificate.p12
echo $APPLE_CERTIFICATE | base64 --decode > certificate.p12
# Debug: Check if certificate file exists and has content
echo "Certificate file info:"
@@ -93,25 +93,25 @@ jobs:
# Debug: Check certificate contents without password (just structure)
echo "Certificate structure (may show error if password protected):"
openssl pkcs12 -info -in certificate.p12 -passin pass:"$APPLE_CERTIFICATE_PASSWORD" -noout || echo "Could not read certificate structure"
openssl pkcs12 -info -in certificate.p12 -passin pass:$APPLE_CERTIFICATE_PASSWORD -noout || echo "Could not read certificate structure"
# Debug: Try to list what's in the p12 file
echo "Attempting to list certificate contents:"
security cms -D -i certificate.p12 || echo "Not a CMS file, trying PKCS12..."
openssl pkcs12 -in certificate.p12 -passin pass:"$APPLE_CERTIFICATE_PASSWORD" -info -nodes 2>&1 | head -20 || echo "Failed to read PKCS12 contents"
openssl pkcs12 -in certificate.p12 -passin pass:$APPLE_CERTIFICATE_PASSWORD -info -nodes 2>&1 | head -20 || echo "Failed to read PKCS12 contents"
# Debug: Extract certificate details to verify type and expiration
echo "Extracting certificate details..."
# Extract just the certificate portion and examine it
openssl pkcs12 -in certificate.p12 -passin pass:"$APPLE_CERTIFICATE_PASSWORD" -nokeys 2>/dev/null | openssl x509 -text -noout | grep -E "(Subject:|Issuer:|Not Before|Not After)" || echo "Failed to parse certificate details"
openssl pkcs12 -in certificate.p12 -passin pass:$APPLE_CERTIFICATE_PASSWORD -nokeys 2>/dev/null | openssl x509 -text -noout | grep -E "(Subject:|Issuer:|Not Before|Not After)" || echo "Failed to parse certificate details"
# Also try to see what type of certificate this is
echo "Certificate type check:"
openssl pkcs12 -in certificate.p12 -passin pass:"$APPLE_CERTIFICATE_PASSWORD" -nokeys 2>/dev/null | openssl x509 -text -noout | grep -E "(CN=|OU=)" || echo "Could not determine certificate type"
openssl pkcs12 -in certificate.p12 -passin pass:$APPLE_CERTIFICATE_PASSWORD -nokeys 2>/dev/null | openssl x509 -text -noout | grep -E "(CN=|OU=)" || echo "Could not determine certificate type"
# Debug: Check if private key exists
echo "Checking for private key..."
openssl pkcs12 -in certificate.p12 -passin pass:"$APPLE_CERTIFICATE_PASSWORD" -nocerts -nodes 2>&1 | grep -E "(PRIVATE KEY|RSA KEY)" | head -5 || echo "No private key found"
openssl pkcs12 -in certificate.p12 -passin pass:$APPLE_CERTIFICATE_PASSWORD -nocerts -nodes 2>&1 | grep -E "(PRIVATE KEY|RSA KEY)" | head -5 || echo "No private key found"
# Create temporary keychain
security create-keychain -p "$KEYCHAIN_PASSWORD" build.keychain
@@ -139,7 +139,6 @@ jobs:
security unlock-keychain -p "$KEYCHAIN_PASSWORD" build.keychain
# Add the keychain to the search list
# shellcheck disable=SC2046
security list-keychains -d user -s build.keychain $(security list-keychains -d user | sed 's/^[[:space:]]*"//;s/"$//')
# List all certificates to debug
@@ -182,11 +181,11 @@ jobs:
security find-certificate -c "Learning Machines" build.keychain -p | openssl x509 -text -noout | grep -E "(Subject:|CN=)" || echo "Could not parse certificate"
# Set the correct signing identity
echo "APPLE_SIGNING_IDENTITY=Developer ID Application: Learning Machines LLC (DJ6XS33FX8)" >> "$GITHUB_ENV"
echo "APPLE_SIGNING_IDENTITY=Developer ID Application: Learning Machines LLC (DJ6XS33FX8)" >> $GITHUB_ENV
echo "Proceeding with identity: Developer ID Application: Learning Machines LLC (DJ6XS33FX8)"
# Also set the team ID
echo "APPLE_DEVELOPMENT_TEAM=DJ6XS33FX8" >> "$GITHUB_ENV"
echo "APPLE_DEVELOPMENT_TEAM=DJ6XS33FX8" >> $GITHUB_ENV
- name: Pre-build frontend to avoid Tauri build issues
run: |
@@ -201,7 +200,6 @@ jobs:
npx nx run artcraft:build
echo "Checking build output..."
# shellcheck disable=SC2012
ls -la apps/artcraft/dist/ | head -10
# Update Tauri config to skip the build (since we already built)
@@ -44,7 +44,6 @@ jobs:
node-version: lts/*
- name: install frontend dependencies
shell: pwsh
run: |
# Navigate to the frontend directory to install dependencies
cd ./frontend
@@ -64,7 +63,6 @@ jobs:
npm list --depth=0 | findstr nx
- name: Pre-build frontend to avoid Tauri build issues
shell: pwsh
run: |
# Build the frontend app from the correct directory
cd ./frontend
-24
View File
@@ -1,24 +0,0 @@
# Builds and tests the workspace on FreeBSD, inside a FreeBSD VM on a Linux runner
# (GitHub has no FreeBSD runners).
name: FreeBSD
# The VM build is slow, so it runs nightly on main and on pushes to `release`, not on pull requests.
on:
schedule:
- cron: "27 4 * * *"
push:
branches: [release]
workflow_dispatch:
concurrency:
group: freebsd-${{ github.ref }}
cancel-in-progress: true
permissions:
contents: read
jobs:
freebsd:
# Intentionally tracked at @main (not SHA-pinned) so org-wide CI improvements, security fixes, and build optimizations propagate without PR churn across all craftapp repos.
uses: storytold/craft-common-actions/.github/workflows/freebsd.yml@main
-28
View File
@@ -1,28 +0,0 @@
name: House rules
on:
push:
branches: [main]
paths:
- AGENTS.md
- CLAUDE.md
- .github/workflows/house-rules.yml
pull_request:
paths:
- AGENTS.md
- CLAUDE.md
- .github/workflows/house-rules.yml
workflow_dispatch:
permissions:
contents: read
concurrency:
group: house-rules-${{ github.ref }}
cancel-in-progress: true
jobs:
house-rules:
uses: storytold/craft-common-actions/.github/workflows/house-rules.yml@main
with:
rust: false
-23
View File
@@ -1,23 +0,0 @@
# Quick static checks for the release pipeline (seconds, no Rust build): workflow syntax,
# packaging shell/PowerShell scripts, and the Linux desktop/AppStream/Flatpak metadata.
name: Packaging lint
on:
push:
branches: [main, release]
paths: [".github/workflows/**", "packaging/**"]
pull_request:
paths: [".github/workflows/**", "packaging/**"]
workflow_dispatch:
permissions:
contents: read
concurrency:
group: packaging-lint-${{ github.ref }}
cancel-in-progress: true
jobs:
lint:
# Intentionally tracked at @main (not SHA-pinned) so org-wide CI improvements, security fixes, and build optimizations propagate without PR churn across all craftapp repos.
uses: storytold/craft-common-actions/.github/workflows/packaging-lint.yml@main
-22
View File
@@ -1,22 +0,0 @@
name: SonarCloud
on:
push:
branches: [main]
pull_request:
types: [opened, synchronize, reopened]
workflow_dispatch:
permissions:
contents: read
concurrency:
group: sonarcloud-${{ github.ref }}
# A push to main is the run that publishes coverage; superseded runs there would never finish.
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
jobs:
sonarcloud:
# Tracked at @main so org-wide CI improvements propagate without PR churn.
uses: storytold/craft-common-actions/.github/workflows/sonarcloud.yml@main
secrets: inherit
-22
View File
@@ -1,25 +1,3 @@
<!-- craft-house-rules:start core source=dde09ea31e2e457f5b4653a6e76f7cdf55944bdf sha256=540e6cea75d12d0710c3dca89658328e81e508ecef102446ca5bb95b7c0b92bb -->
## House rules
These apply in every Craft repository. A later rule may be stricter, not weaker. The source is `standards/house-rules-core.md` in `storytold/craft-contrib`. Propose changes there, not in a copy.
1. **A person is accountable.** Whoever runs an agent answers for what it opens and for its review comments.
2. **Check, then claim.** Before writing code, read the issue comments, search pull requests for `#N` and the keywords, and check recent commits on `main`. Comment on the issue first. Release a claim you will not finish.
3. **Verify, don't guess.** Open a definition before using it. Confirm the cause in the code, even when someone names a location.
4. **Keep fixes narrow and tested.** Fix what the issue describes, where the bug is. Add a test that fails without the fix.
5. **Review the diff before opening a pull request.** Identifiers exist as used, the test fails without the fix, nothing unrelated changed, and the description is true.
6. **Look for a better pull request.** If another fixes the same thing, close yours when it is better or equal, and say why.
7. **Report only what ran.** Say what you tested and what you could not run. Without a local build or test, say so and rely on CI before marking ready.
8. **`main` changes through a pull request.** Do not push to it. Branch protection enforces this. There is no exception.
9. **Stop and ask only about** licensing outside the allowlist, publishing, releases, a new remote, secrets, spending money, destructive git, and anything else hard to undo. Otherwise keep working. `plan/` is a gitignored notebook, not rules. Without it, do not ask for it and do not stop.
10. **Keep secrets and personal details out** of the repository, logs and requests.
11. **CI stays shared.** Workflows call `storytold/craft-common-actions`. Do not change CI in a feature or fix pull request. If CI cannot cover a change, open an issue there.
12. **Guards say why.** A check that looks removable says what it prevents. Read that before changing it.
13. **Instructions must be readable by everyone.** No notes about one machine, and no links to files a reader cannot open. `craftrules` is retired. Do not link to it.
14. **Keep pull requests moving.** Concurrent pull requests are fine; unanswered actionable feedback is not. Respond in 7 days, a reminder at 14, closure eligible at 30, from when an actionable request waits on the author. Suggestions and unconfirmed bot findings do not start the clock. A fix, explanation or agreed plan resets it; unrelated activity does not. Waiting on a reviewer or maintainer pauses it and does not count against the author. A maintainer decides closure; it is never automatic. Reuse search results, do not poll, and stop at a rate limit.
15. **No agent attribution.** A pull request does not say an agent wrote it. A repository rule may not require that.
<!-- craft-house-rules:end -->
# AGENTS.md
This file provides guidance for coding agents when working with the Artcraft monorepo.
-1
View File
@@ -1 +0,0 @@
@AGENTS.md
Symlink
+1
View File
@@ -0,0 +1 @@
AGENTS.md
+2 -8
View File
@@ -1,8 +1,4 @@
<h1 align="center">ArtCraft</h1>
<p align="center">
<a href="https://sonarcloud.io/project/overview?id=storytold_artcraft"><img alt="Scanned by SonarCloud" src="https://img.shields.io/badge/Scanned_by-SonarCloud-4b9fd5?style=flat-square&logo=sonarqubecloud&logoColor=white"></a>
</p>
<p align="center"><strong>The IDE for artists.</strong></p>
<!-- Main video: https://www.youtube.com/watch?v=kzvQMdg66Go
@@ -130,15 +126,13 @@ Additional provider integrations are planned for Kling, Google, Runway, and Luma
## Downloads
- [Stable releases for Windows, macOS, and Linux](https://getartcraft.com/)
- [Latest builds on GitHub](https://github.com/storytold/artcraft/releases) (including AppImage, DEB, RPM, Arch, and Flatpak packages)
- [Linux packaging notes](./packaging/linux/README.md)
- [Stable releases for Windows and macOS](https://getartcraft.com/)
- [Latest builds on GitHub](https://github.com/storytold/artcraft/releases)
- [Build from source](./_docs/dev_setup.md), including Linux
## Documentation
- [Development setup](./_docs/dev_setup.md)
- [Linux packaging](./packaging/linux/README.md)
- [Developer documentation](./_docs)
- [Desktop build and development scripts](./script/artcraft)
- [Roadmap](./ROADMAP.md)
+1 -1
View File
@@ -34,7 +34,7 @@ log.workspace = true
open = "5"
reqwest = { workspace = true, features = ["multipart"] }
resvg = "0.45"
rfd = { version = "0.16", default-features = false, features = ["gtk3"] }
rfd = "0.16"
rodio = { version = "0.20", default-features = false, features = ["symphonia-all"] }
serde.workspace = true
serde_json.workspace = true
+14 -29
View File
@@ -1,20 +1,7 @@
use std::env;
use std::path::{Path, PathBuf};
use std::path::PathBuf;
pub fn main() {
println!("cargo:rerun-if-env-changed=DATABASE_URL");
println!("cargo:rerun-if-env-changed=SQLX_OFFLINE");
if let Ok(url) = env::var("DATABASE_URL") {
println!("cargo:rustc-env=DATABASE_URL={}", url);
return;
}
if env::var("SQLX_OFFLINE").as_deref() == Ok("true") || env::var("SQLX_OFFLINE").as_deref() == Ok("1") {
println!("cargo:rustc-env=SQLX_OFFLINE=true");
return;
}
// NB: This should help JetBrains' RustRover from highlighting failing query macros,
// but we do not want to interfere with the following:
//
@@ -27,29 +14,27 @@ pub fn main() {
Some("unix") => unix_temp_database_pathing(),
Some("windows") => windows_temp_database_pathing(),
_ => {
println!("cargo:rustc-env=SQLX_OFFLINE=true");
println!("cargo:warning=Unsupported target family, using Unix temp database pathing.");
unix_temp_database_pathing();
},
}
}
fn unix_temp_database_pathing() {
println!("cargo:rerun-if-changed=/tmp/tasks.sqlite");
if Path::new("/tmp/tasks.sqlite").exists() {
println!("cargo:rustc-env=DATABASE_URL=sqlite:/tmp/tasks.sqlite");
} else {
println!("cargo:rustc-env=SQLX_OFFLINE=true");
}
println!("cargo:rustc-env=DATABASE_URL=sqlite:/tmp/tasks.sqlite");
}
fn windows_temp_database_pathing() {
if let Ok(local_app_data) = env::var("LOCALAPPDATA") {
let path = PathBuf::from(local_app_data).join("Temp\\tasks.sqlite");
if path.exists() {
let path_str = path.to_str().expect("path should be valid");
println!("cargo:warning=LocalAppData path: {}", path_str);
println!("cargo:rustc-env=DATABASE_URL=sqlite:{}", path_str);
return;
}
let path = PathBuf::from(local_app_data);
let path = path.join("Temp\\tasks.sqlite");
let path = path
.to_str()
.expect("path should be valid")
.to_string();
println!("cargo:warning=LocalAppData path: {}", path);
println!("cargo:rustc-env=DATABASE_URL=sqlite:{}", path);
} else {
panic!("LOCALAPPDATA environment variable not set");
}
println!("cargo:rustc-env=SQLX_OFFLINE=true");
}
-36
View File
@@ -1,36 +0,0 @@
# ArtCraft Studio Linux releases
The Linux workflow builds every package before the release job signs and uploads them to the
existing `artcraft-v<version>` draft, retaining Windows and macOS assets. The version comes from
`crates/desktop/artcraft/tauri.conf.json`.
| Architecture | Outputs | Builder |
|--------------|---------------------------------------------------------------|----------------------------|
| x86_64 | AppImage, DEB, RPM, tar.gz, Arch pkg.tar.zst, Flatpak bundle, OCI | Ubuntu 22.04 native |
| aarch64 | AppImage, DEB, RPM, tar.gz, Arch pkg.tar.zst, Flatpak bundle, OCI | Ubuntu 22.04 ARM native |
| riscv64 | DEB, RPM, tar.gz | Ubuntu 24.04 cross compiler |
Native AppImages also include zsync delta-update metadata.
Flatpak uses native Ubuntu 24.04 runners and GNOME 51 for GTK3/WebKitGTK 4.1. Its build checks
for missing ELF dependencies inside the sandbox. RISC-V packages require Ubuntu 24.04-era
libraries; x86_64 and ARM64 use Ubuntu 22.04 as their build baseline. The suite does not publish
RISC-V AppImages, Arch packages or Flatpaks.
`package.sh <architecture> <rust-target>` normalizes Tauri's DEB/RPM/AppImage filenames and
extracts the DEB's `/usr` tree into the suite's `bin/` and `share/` tarball layout. Shared Craft
Arch packaging consumes that tarball, with WebKitGTK, GTK3 and OpenSSL dependencies added.
`flatpak.sh` consumes the same tarball, builds the bundle, and exports the OCI layout.
Release runs import `LINUX_SIGNING_KEY` for Flatpak commit signatures and OCI publishing. The final shared
`draft-release` action signs RPMs, DEBs, Arch packages, AppImages, tarballs, OCI manifests and
checksums. Only a stable version on `release` or `artcraft-release` moves the OCI stable architecture tags.
Like the macOS and Windows release workflows, Linux release packaging runs on release branches
(`release`, `artcraft-release`, `artcraft-linux`) and workflow dispatch rather than on pull requests.
APT, RPM and pacman repositories are generated by `artcraft-services` from the release assets
and checksums. Its release catalog must be updated only after the signed release is published.
Installing a DEB manually is separate from publishing that DEB in the website's APT repository.
A successful build proves artifact production and metadata/dependency checks. It does not replace
installation and interactive testing on physical RISC-V, ARM64 and x86_64 desktops.
@@ -1,14 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<component type="desktop-application">
<id>ai.artcraft.app</id>
<name>ArtCraft Studio</name>
<summary>Create AI images and video</summary>
<metadata_license>MIT</metadata_license>
<project_license>MIT OR Apache-2.0</project_license>
<description><p>ArtCraft Studio is a desktop workspace for generating AI images and video with connected AI providers.</p></description>
<launchable type="desktop-id">ai.artcraft.app.desktop</launchable>
<url type="homepage">https://getartcraft.com</url>
<developer id="ai.artcraft"><name>ArtCraft</name></developer>
<categories><category>Graphics</category><category>AudioVideo</category></categories>
<provides><binary>artcraft</binary></provides>
</component>
-40
View File
@@ -1,40 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
arch="$(uname -m)"
version="$(jq -r .version crates/desktop/artcraft/tauri.conf.json)"
base="artcraft-$version-linux-$arch"
work="$(mktemp -d)"
trap 'rm -rf "$work"' EXIT
mkdir -p "$work/stage" dist/release
tar -xzf "dist/linux/$base.tar.gz" --strip-components=1 -C "$work/stage"
cp packaging/linux/flatpak.yml "$work/manifest.yml"
flatpak remote-add --user --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo
sign=()
keys=()
if [[ -n "${SIGNING_FPR:-}" ]]; then
sign=(--gpg-sign="$SIGNING_FPR" --gpg-homedir="$GNUPGHOME")
gpg --batch --armor --export "$SIGNING_FPR" > "$work/public.asc"
keys=(--gpg-keys="$work/public.asc")
fi
flatpak-builder --user --install-deps-from=flathub --disable-rofiles-fuse --force-clean \
--default-branch=stable --repo="$work/repo" "${sign[@]}" "$work/build" "$work/manifest.yml"
# Fail on missing WebKitGTK or other runtime libraries rather than shipping an unloadable binary.
# shellcheck disable=SC2016 # Expand these variables inside the Flatpak runtime shell.
flatpak build --runtime "$work/build" sh -c '
dependencies=$(ldd /app/bin/artcraft) || exit 1
printf "%s\n" "$dependencies"
case "$dependencies" in *"not found"*) exit 1 ;; esac
'
flatpak build-bundle --runtime-repo=https://dl.flathub.org/repo/flathub.flatpakrepo \
"${keys[@]}" "$work/repo" "dist/release/$base.flatpak" ai.artcraft.app stable
flatpak build-bundle --oci "$work/repo" "dist/release/$base-oci" ai.artcraft.app stable
skopeo inspect "oci:dist/release/$base-oci" >/dev/null
if [[ -n "${SIGNING_FPR:-}" ]]; then
registry="ghcr.io/${GITHUB_REPOSITORY,,}"
creds="$GITHUB_ACTOR:$GITHUB_TOKEN"
skopeo copy --format oci --dest-creds "$creds" "oci:dist/release/$base-oci" "docker://$registry:$version-$arch"
if [[ "$version" != *-* && ( "$GITHUB_REF" == refs/heads/release || "$GITHUB_REF" == refs/heads/artcraft-release ) ]]; then
skopeo copy --format oci --dest-creds "$creds" "oci:dist/release/$base-oci" "docker://$registry:stable-$arch"
fi
skopeo inspect --raw --creds "$creds" "docker://$registry:$version-$arch" >"dist/release/$base.oci-manifest.json"
fi
-25
View File
@@ -1,25 +0,0 @@
app-id: ai.artcraft.app
runtime: org.gnome.Platform
runtime-version: '51'
sdk: org.gnome.Sdk
command: artcraft
finish-args:
- --socket=wayland
- --socket=fallback-x11
- --socket=pulseaudio
- --share=ipc
- --share=network
- --device=dri
- --filesystem=xdg-pictures
- --filesystem=xdg-videos
- --filesystem=xdg-download
modules:
- name: artcraft
buildsystem: simple
build-commands:
- cp -a . /app/
- install -Dm644 /app/share/icons/hicolor/128x128/apps/artcraft.png /app/share/icons/hicolor/128x128/apps/ai.artcraft.app.png
- sed -i 's/^Icon=.*/Icon=ai.artcraft.app/' /app/share/applications/ai.artcraft.app.desktop
sources:
- type: dir
path: stage
-50
View File
@@ -1,50 +0,0 @@
#!/usr/bin/env bash
# Integration fixture: real DEB/RPM archives, with no Rust compilation or signing key.
set -euo pipefail
script="$(realpath "$(dirname "$0")/package.sh")"
work="$(mktemp -d)"
trap 'rm -rf "$work"' EXIT
cd "$work"
mkdir -p crates/desktop/artcraft target/x86_64-unknown-linux-gnu/release/bundle/{deb,rpm,appimage}
printf '{"version":"0.42.0"}\n' > crates/desktop/artcraft/tauri.conf.json
mkdir -p stage/{DEBIAN,usr/bin,usr/share/applications}
cp /bin/true stage/usr/bin/artcraft
printf '[Desktop Entry]\nType=Application\nName=ArtCraft\nExec=artcraft\n' > stage/usr/share/applications/ArtCraft.desktop
cat > stage/DEBIAN/control <<'CONTROL'
Package: artcraft
Version: 0.42.0
Architecture: amd64
Maintainer: Fixture <fixture@example.invalid>
Description: Packaging fixture
CONTROL
dpkg-deb --build stage target/x86_64-unknown-linux-gnu/release/bundle/deb/ArtCraft.deb
mkdir -p rpmbuild/{BUILD,RPMS,SOURCES,SPECS,SRPMS}
cat > rpmbuild/SPECS/artcraft.spec <<'SPEC'
Name: artcraft
Version: 0.42.0
Release: 1
Summary: Packaging fixture
License: MIT
%description
Packaging fixture
%install
mkdir -p %{buildroot}/usr/bin
cp /bin/true %{buildroot}/usr/bin/artcraft
%files
/usr/bin/artcraft
SPEC
rpmbuild --define "_topdir $work/rpmbuild" -bb rpmbuild/SPECS/artcraft.spec
find rpmbuild/RPMS -name '*.rpm' -exec cp {} target/x86_64-unknown-linux-gnu/release/bundle/rpm/ \;
printf 'appimage fixture\n' > target/x86_64-unknown-linux-gnu/release/bundle/appimage/ArtCraft.AppImage
"$script" x86_64 x86_64-unknown-linux-gnu
for format in deb rpm AppImage AppImage.zsync tar.gz; do test -s "dist/release/artcraft-0.42.0-linux-x86_64.$format"; done
mkdir unpacked
tar -xzf dist/release/artcraft-0.42.0-linux-x86_64.tar.gz -C unpacked
test -x unpacked/artcraft-0.42.0-linux-x86_64/bin/artcraft
test -f unpacked/artcraft-0.42.0-linux-x86_64/share/applications/ai.artcraft.app.desktop
rm target/x86_64-unknown-linux-gnu/release/bundle/rpm/*.rpm
if "$script" x86_64 x86_64-unknown-linux-gnu; then
echo 'Packaging accepted a missing RPM' >&2
exit 1
fi
echo 'Package integration checks passed'
-54
View File
@@ -1,54 +0,0 @@
#!/usr/bin/env bash
# Normalize Tauri packages and derive the suite's bin/share tarball from its Debian package.
set -euo pipefail
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
arch="${1:?architecture required}"
target="${2:?Rust target required}"
version="$(jq -r .version crates/desktop/artcraft/tauri.conf.json)"
case "$arch" in x86_64|aarch64|riscv64) ;; *) exit 2 ;; esac
base="artcraft-$version-linux-$arch"
bundle="target/$target/release/bundle"
mkdir -p dist/release
for format in deb rpm AppImage; do
mapfile -t files < <(find "$bundle" -type f -name "*.$format")
if [[ "$arch" == riscv64 && "$format" == AppImage ]]; then continue; fi
[[ ${#files[@]} == 1 ]] || { echo "Expected one $format in $bundle, found ${#files[@]}" >&2; exit 1; }
cp "${files[0]}" "dist/release/$base.$format"
done
if [[ "$arch" != riscv64 ]]; then
zsyncmake -u "https://github.com/storytold/artcraft/releases/download/artcraft-v$version/$base.AppImage" \
-o "dist/release/$base.AppImage.zsync" "dist/release/$base.AppImage"
fi
work="$(mktemp -d)"
trap 'rm -rf "$work"' EXIT
dpkg-deb -x "dist/release/$base.deb" "$work/deb"
mkdir -p "$work/$base"
cp -a "$work/deb/usr/." "$work/$base/"
mapfile -t desktops < <(find "$work/$base/share/applications" -name '*.desktop')
[[ ${#desktops[@]} == 1 ]] || { echo 'Expected one desktop entry' >&2; exit 1; }
if [[ "${desktops[0]}" != "$work/$base/share/applications/ai.artcraft.app.desktop" ]]; then
mv "${desktops[0]}" "$work/$base/share/applications/ai.artcraft.app.desktop"
fi
install -Dm644 "$script_dir/ai.artcraft.app.metainfo.xml" "$work/$base/share/metainfo/ai.artcraft.app.metainfo.xml"
sed -i 's/^Name=.*/Name=ArtCraft Studio/' "$work/$base/share/applications/ai.artcraft.app.desktop"
test -x "$work/$base/bin/artcraft"
case "$arch" in
x86_64) machine='Advanced Micro Devices X86-64' ;;
aarch64) machine='AArch64' ;;
riscv64) machine='RISC-V' ;;
esac
readelf -h "$work/$base/bin/artcraft" | grep -F "$machine"
if [[ "$arch" == "$(uname -m)" ]]; then
dependencies="$(ldd "$work/$base/bin/artcraft")"
if grep -q 'not found' <<< "$dependencies"; then
printf '%s\n' "$dependencies" >&2
exit 1
fi
fi
# The tarball layout is consumed by the launcher, Arch packaging and Flatpak.
tar -czf "dist/release/$base.tar.gz" -C "$work" "$base"
case "$arch" in x86_64) deb_arch=amd64 ;; aarch64) deb_arch=arm64 ;; riscv64) deb_arch=riscv64 ;; esac
[[ "$(dpkg-deb -f "dist/release/$base.deb" Architecture)" == "$deb_arch" ]]
[[ "$(rpm -qp --qf '%{ARCH}' "dist/release/$base.rpm")" == "$arch" ]]
dpkg-deb --info "dist/release/$base.deb"
rpm -qp --requires "dist/release/$base.rpm"
-7
View File
@@ -1,7 +0,0 @@
# boring-sys2 has built-in ARM toolchains but no RISC-V toolchain. Set the target
# explicitly so BoringSSL does not select the x86_64 runner's assembly sources.
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR riscv64)
set(CMAKE_C_COMPILER riscv64-linux-gnu-gcc)
set(CMAKE_CXX_COMPILER riscv64-linux-gnu-g++)
set(OPENSSL_NO_ASM ON CACHE BOOL "Use BoringSSL's portable RISC-V implementation")
-15
View File
@@ -1,15 +0,0 @@
{
"build": {
"beforeBuildCommand": ""
},
"bundle": {
"category": "GraphicsAndDesign",
"shortDescription": "AI image and video creation",
"longDescription": "ArtCraft Studio is a desktop workspace for generating AI images and video.",
"linux": {
"appimage": {
"bundleMediaFramework": true
}
}
}
}
-3
View File
@@ -1,3 +0,0 @@
# Windows Packaging
ArtCraft Studio Windows release packaging assets and configuration.
-2
View File
@@ -1,2 +0,0 @@
sonar.projectKey=storytold_artcraft
sonar.organization=storytold