From af7ef0404844252de2ceb08a46bb7081df340090 Mon Sep 17 00:00:00 2001 From: Sylvain Cau Date: Sat, 21 Feb 2026 10:33:12 -0800 Subject: [PATCH] chore: prepare repository for open-source release Migrate CI/CD from self-hosted ARC runners to GitHub-hosted runners: - Replace arc-runner-codex with ubuntu-latest/ubuntu-22.04 - Remove container blocks, apt-get installs, and manual Rust setup - Replace PVC cache with upload/download-artifact - Re-enable build provenance attestation and npm --provenance - Restore full cross-platform dist targets in Cargo.toml - Remove leftover Gitea workflow files Update documentation for public repository: - Add active development notice to README - Replace userjot.com feature board links with GitHub Issues - Fix broken config link in README - Update doc site navbar and footer links - Add GitHub issue templates for bug reports and feature requests --- .gitea/workflows/build.yaml | 859 --------------------- .gitea/workflows/ci.yaml | 271 ------- .gitea/workflows/docs.yaml | 66 -- .github/ISSUE_TEMPLATE/bug_report.yml | 98 +++ .github/ISSUE_TEMPLATE/config.yml | 5 + .github/ISSUE_TEMPLATE/feature_request.yml | 55 ++ .github/workflows/build.yml | 606 ++++----------- .github/workflows/ci.yml | 158 +--- Cargo.toml | 24 +- README.md | 29 +- docs/api/openapi.json | 2 +- docs/dev/intro.md | 2 +- docs/docs/intro.md | 2 +- docs/docs/troubleshooting.md | 2 +- docs/docusaurus.config.ts | 14 +- docs/src/pages/index.tsx | 2 +- src/api/docs.rs | 2 +- web/openapi.json | 2 +- 18 files changed, 339 insertions(+), 1860 deletions(-) delete mode 100644 .gitea/workflows/build.yaml delete mode 100644 .gitea/workflows/ci.yaml delete mode 100644 .gitea/workflows/docs.yaml create mode 100644 .github/ISSUE_TEMPLATE/bug_report.yml create mode 100644 .github/ISSUE_TEMPLATE/config.yml create mode 100644 .github/ISSUE_TEMPLATE/feature_request.yml diff --git a/.gitea/workflows/build.yaml b/.gitea/workflows/build.yaml deleted file mode 100644 index 1b09e528..00000000 --- a/.gitea/workflows/build.yaml +++ /dev/null @@ -1,859 +0,0 @@ -# Unified CI/CD workflow for Codex (Gitea) -# -# Based on cargo-dist with custom additions for testing, linting, frontend, and Docker. -# -# Triggers: -# - Push to main: test, lint, build artifacts, push Docker with 'main' tag -# - Push version tag: test, lint, build artifacts, push Docker with version tags, create release -# -# Note: PRs are handled by ci.yaml (test, lint, frontend, Docker build validation) - -name: Build - -permissions: - contents: write - packages: write - -on: - push: - branches: - - main - tags: - - "**[0-9]+.[0-9]+.[0-9]+*" - -# Cancel in-progress runs for same branch/tag -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -# Shared cache directory on mount available to runners -# Each workflow run gets its own isolated directory under /cache/ci -env: - CI_CACHE: /cache/ci/${{ github.run_id }} - -jobs: - # Build test binaries and create nextest archive - build-tests: - name: Build Tests - runs-on: ubuntu-latest - container: - image: catthehacker/ubuntu:act-latest - volumes: - - /cache:/cache - timeout-minutes: 60 - env: - CARGO_INCREMENTAL: "0" - # Limit parallel rustc jobs to reduce peak memory (OOMKill prevention) - CARGO_BUILD_JOBS: "1" - # Strip debug info to reduce memory usage during compilation and linking - CARGO_PROFILE_TEST_DEBUG: "0" - steps: - - name: Install base dependencies - run: | - apt-get update - apt-get install -y build-essential pkg-config clang mold - - uses: actions/checkout@v4 - - name: Install Rust - uses: dtolnay/rust-toolchain@stable - - name: Install cargo-nextest - uses: taiki-e/install-action@nextest - - name: Show memory info - run: | - echo "=== Memory Info ===" - free -h || cat /proc/meminfo || echo "Cannot determine memory" - echo "=== CPU Info ===" - nproc || echo "Cannot determine CPU count" - - name: Prepare cache directory - run: | - mkdir -p $CI_CACHE - if test -d $CI_CACHE; then - echo "CI_CACHE directory ready: $CI_CACHE" - ls -la $(dirname $CI_CACHE) - else - echo "::error::CI_CACHE directory $CI_CACHE is not available. Is the PVC mounted?" - exit 1 - fi - - name: Build and archive tests - run: | - cargo nextest archive --features rar --archive-file $CI_CACHE/nextest-archive.tar.zst - echo "=== Post-build memory ===" - free -h || cat /proc/meminfo || true - echo "=== Verifying archive ===" - ls -lh $CI_CACHE/nextest-archive.tar.zst || { echo "::error::Failed to write nextest archive to cache"; exit 1; } - # Replaced by /cache PVC - uncomment to revert to artifact storage - # - name: Upload test archive - # uses: actions/upload-artifact@v3 - # with: - # name: nextest-archive - # path: nextest-archive.tar.zst - # retention-days: 1 - - # Run tests in parallel partitions - test: - name: Test (${{ matrix.partition }}/5) - needs: build-tests - runs-on: ubuntu-latest - container: - image: catthehacker/ubuntu:act-latest - volumes: - - /cache:/cache - timeout-minutes: 60 - strategy: - fail-fast: false - matrix: - partition: [1, 2, 3, 4, 5] - steps: - - uses: actions/checkout@v4 - - name: Install Rust - uses: dtolnay/rust-toolchain@stable - - name: Install cargo-nextest - uses: taiki-e/install-action@nextest - # Replaced by /cache PVC - uncomment to revert to artifact storage - # - name: Download test archive - # uses: actions/download-artifact@v3 - # with: - # name: nextest-archive - - name: Run tests (partition ${{ matrix.partition }}/5) - run: cargo nextest run --archive-file $CI_CACHE/nextest-archive.tar.zst --partition hash:${{ matrix.partition }}/5 - - # Run linting checks - lint: - name: Lint - runs-on: ubuntu-latest - container: - image: catthehacker/ubuntu:act-latest - timeout-minutes: 30 - steps: - - name: Install base dependencies - run: | - apt-get update - apt-get install -y build-essential pkg-config clang mold - - uses: actions/checkout@v4 - - name: Install Rust - uses: dtolnay/rust-toolchain@stable - with: - components: rustfmt, clippy - - name: Format check - run: cargo fmt -- --check - - name: Clippy check - run: cargo clippy --features rar -- -D warnings - - # Run frontend tests and build - frontend: - name: Frontend - runs-on: ubuntu-latest - container: - image: catthehacker/ubuntu:act-latest - volumes: - - /cache:/cache - timeout-minutes: 30 - steps: - - uses: actions/checkout@v4 - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: "22" - # Note: We remove package-lock.json and use npm install instead of npm ci - # because Biome updates frequently and we want to use the latest compatible version - - name: Install dependencies - working-directory: web - run: | - rm -rf node_modules package-lock.json - npm install - - name: Lint check - working-directory: web - run: npm run lint - - name: Run tests - working-directory: web - run: npm run test:run - - name: Build frontend - working-directory: web - run: npm run build - - name: Prepare cache directory - run: | - mkdir -p $CI_CACHE - if test -d $CI_CACHE; then - echo "CI_CACHE directory ready: $CI_CACHE" - ls -la $(dirname $CI_CACHE) - else - echo "::error::CI_CACHE directory $CI_CACHE is not available. Is the PVC mounted?" - exit 1 - fi - - name: Cache frontend build - run: | - cp -r web/dist $CI_CACHE/frontend-dist - ls -lh $CI_CACHE/frontend-dist/ || { echo "::error::Failed to cache frontend build"; exit 1; } - # Replaced by /cache PVC - uncomment to revert to artifact storage - # - name: Upload frontend build - # uses: actions/upload-artifact@v3 - # with: - # name: frontend-dist - # path: web/dist - # retention-days: 1 - - # Run plugin checks (lint, typecheck, tests) - plugins: - name: Plugins (${{ matrix.plugin }}) - runs-on: ubuntu-latest - container: - image: catthehacker/ubuntu:act-latest - timeout-minutes: 30 - strategy: - fail-fast: false - matrix: - plugin: - - sdk-typescript - - metadata-echo - - metadata-mangabaka - - metadata-openlibrary - - recommendations-anilist - - sync-anilist - steps: - - uses: actions/checkout@v4 - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: "22" - # SDK must be built first for other plugins to use - - name: Build SDK (dependency) - if: matrix.plugin != 'sdk-typescript' - working-directory: plugins/sdk-typescript - run: | - rm -rf node_modules package-lock.json - npm install - npm run build - - name: Install dependencies - working-directory: plugins/${{ matrix.plugin }} - run: | - rm -rf node_modules package-lock.json - npm install - - name: Lint - working-directory: plugins/${{ matrix.plugin }} - run: npm run lint - - name: Typecheck - working-directory: plugins/${{ matrix.plugin }} - run: npx tsc --noEmit - - name: Test - working-directory: plugins/${{ matrix.plugin }} - run: npm run test - - name: Build - working-directory: plugins/${{ matrix.plugin }} - run: npm run build - - # Run 'dist plan' (or host) to determine what tasks we need to do - plan: - name: Plan - needs: [test, lint, frontend, plugins] - runs-on: ubuntu-latest - container: - image: catthehacker/ubuntu:act-latest - volumes: - - /cache:/cache - outputs: - val: ${{ steps.plan.outputs.manifest }} - tag: ${{ steps.info.outputs.tag }} - tag-flag: ${{ steps.info.outputs.tag-flag }} - publishing: ${{ steps.info.outputs.publishing }} - is-release: ${{ steps.info.outputs.is-release }} - version: ${{ steps.info.outputs.version }} - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - steps: - - uses: actions/checkout@v4 - with: - submodules: recursive - - name: Determine build info - id: info - shell: bash - run: | - # Check if this is a tag push (release), main branch push, or PR - if [[ "${{ github.ref }}" == refs/tags/* ]]; then - TAG_NAME="${{ github.ref_name }}" - VERSION="${TAG_NAME#v}" - echo "is-release=true" >> "$GITHUB_OUTPUT" - echo "tag=$TAG_NAME" >> "$GITHUB_OUTPUT" - echo "tag-flag=--tag=$TAG_NAME" >> "$GITHUB_OUTPUT" - echo "publishing=true" >> "$GITHUB_OUTPUT" - echo "version=$VERSION" >> "$GITHUB_OUTPUT" - elif [[ "${{ github.ref }}" == refs/heads/main ]]; then - echo "is-release=false" >> "$GITHUB_OUTPUT" - echo "tag=" >> "$GITHUB_OUTPUT" - echo "tag-flag=" >> "$GITHUB_OUTPUT" - echo "publishing=true" >> "$GITHUB_OUTPUT" - echo "version=main" >> "$GITHUB_OUTPUT" - else - # Pull request - echo "is-release=false" >> "$GITHUB_OUTPUT" - echo "tag=" >> "$GITHUB_OUTPUT" - echo "tag-flag=" >> "$GITHUB_OUTPUT" - echo "publishing=false" >> "$GITHUB_OUTPUT" - echo "version=dev" >> "$GITHUB_OUTPUT" - fi - - name: Install Rust - uses: dtolnay/rust-toolchain@stable - - name: Install dist - shell: bash - run: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/v0.30.3/cargo-dist-installer.sh | sh" - - name: Prepare cache directory - run: | - mkdir -p $CI_CACHE - if test -d $CI_CACHE; then - echo "CI_CACHE directory ready: $CI_CACHE" - ls -la $(dirname $CI_CACHE) - else - echo "::error::CI_CACHE directory $CI_CACHE is not available. Is the PVC mounted?" - exit 1 - fi - - name: Cache dist binary - run: | - cp ~/.cargo/bin/dist $CI_CACHE/dist - ls -lh $CI_CACHE/dist || { echo "::error::Failed to cache dist binary"; exit 1; } - # Replaced by /cache PVC - uncomment to revert to artifact storage - # - name: Cache dist - # uses: actions/upload-artifact@v3 - # with: - # name: cargo-dist-cache - # path: ~/.cargo/bin/dist - - name: Run dist plan - id: plan - shell: bash - run: | - if [[ "${{ steps.info.outputs.is-release }}" == "true" ]]; then - dist host --steps=create --tag=${{ steps.info.outputs.tag }} --output-format=json > plan-dist-manifest.json - else - dist plan --output-format=json > plan-dist-manifest.json - fi - echo "dist ran successfully" - cat plan-dist-manifest.json - echo "manifest=$(jq -c "." plan-dist-manifest.json)" >> "$GITHUB_OUTPUT" - - name: Cache dist-manifest.json - run: | - cp plan-dist-manifest.json $CI_CACHE/plan-dist-manifest.json - ls -lh $CI_CACHE/plan-dist-manifest.json || { echo "::error::Failed to cache dist manifest"; exit 1; } - # Replaced by /cache PVC - uncomment to revert to artifact storage - # - name: "Upload dist-manifest.json" - # uses: actions/upload-artifact@v3 - # with: - # name: artifacts-plan-dist-manifest - # path: plan-dist-manifest.json - - # Build and packages all the platform-specific things - build-local-artifacts: - name: build-local-artifacts (${{ join(matrix.targets, ', ') }}) - # Let the initial task tell us to not run (currently very blunt) - needs: - - plan - if: ${{ fromJson(needs.plan.outputs.val).ci.github.artifacts_matrix.include != null && (needs.plan.outputs.publishing == 'true' || fromJson(needs.plan.outputs.val).ci.github.pr_run_mode == 'upload') }} - strategy: - fail-fast: false - # Target platforms/runners are computed by dist in create-release. - # Each member of the matrix has the following arguments: - # - # - runner: the github runner - # - dist-args: cli flags to pass to dist - # - install-dist: expression to run to install dist on the runner - # - # Typically there will be: - # - 1 "global" task that builds universal installers - # - N "local" tasks that build each platform's binaries and platform-specific installers - matrix: ${{ fromJson(needs.plan.outputs.val).ci.github.artifacts_matrix }} - runs-on: ${{ matrix.runner }} - container: - image: ${{ matrix.container && matrix.container.image || 'catthehacker/ubuntu:act-latest' }} - volumes: - - /cache:/cache - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - BUILD_MANIFEST_NAME: target/distrib/${{ join(matrix.targets, '-') }}-dist-manifest.json - steps: - - name: enable longpaths - run: | - git config --global core.longpaths true - - uses: actions/checkout@v4 - with: - submodules: recursive - - name: Install Rust non-interactively if not already installed - if: ${{ matrix.container }} - run: | - if ! command -v cargo > /dev/null 2>&1; then - curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y - echo "$HOME/.cargo/bin" >> $GITHUB_PATH - fi - - name: Install dist - run: ${{ matrix.install_dist.run }} - # Restore cached artifacts from /cache PVC - - name: Restore plan manifest from cache - run: | - mkdir -p target/distrib/ - cp $CI_CACHE/plan-dist-manifest.json target/distrib/ - - name: Restore frontend dist from cache - run: | - mkdir -p web/dist/ - cp -r $CI_CACHE/frontend-dist/* web/dist/ - # Replaced by /cache PVC - uncomment to revert to artifact storage - # - name: Fetch local artifacts - # uses: actions/download-artifact@v3 - # with: - # pattern: artifacts-* - # path: target/distrib/ - # merge-multiple: true - # - name: Download frontend dist - # uses: actions/download-artifact@v3 - # with: - # name: frontend-dist - # path: web/dist/ - - name: Install dependencies - run: | - ${{ matrix.packages_install }} - - name: Build artifacts - run: | - # Actually do builds and make zips and whatnot - dist build ${{ needs.plan.outputs.tag-flag }} --print=linkage --output-format=json ${{ matrix.dist_args }} > dist-manifest.json - echo "dist ran successfully" - - id: cargo-dist - name: Post-build - # We force bash here just because github makes it really hard to get values up - # to "real" actions without writing to env-vars, and writing to env-vars has - # inconsistent syntax between shell and powershell. - shell: bash - run: | - # Parse out what we just built and upload it to scratch storage - echo "paths<> "$GITHUB_OUTPUT" - dist print-upload-files-from-manifest --manifest dist-manifest.json >> "$GITHUB_OUTPUT" - echo "EOF" >> "$GITHUB_OUTPUT" - - cp dist-manifest.json "$BUILD_MANIFEST_NAME" - - name: Cache build artifacts - shell: bash - run: | - CACHE_DIR=$CI_CACHE/artifacts-build-local-${{ join(matrix.targets, '_') }} - mkdir -p "$CACHE_DIR" - if test -d "$CACHE_DIR"; then - echo "Cache directory ready: $CACHE_DIR" - else - echo "::error::Cache directory $CACHE_DIR is not available. Is the PVC mounted?" - exit 1 - fi - # Copy upload files listed by dist - while IFS= read -r f; do - cp "$f" "$CACHE_DIR/" - done <<< "${{ steps.cargo-dist.outputs.paths }}" - cp "$BUILD_MANIFEST_NAME" "$CACHE_DIR/" - echo "Cached build artifacts:" - ls -lh "$CACHE_DIR/" - # Replaced by /cache PVC - uncomment to revert to artifact storage - # - name: "Upload artifacts" - # uses: actions/upload-artifact@v3 - # with: - # name: artifacts-build-local-${{ join(matrix.targets, '_') }} - # path: | - # ${{ steps.cargo-dist.outputs.paths }} - # ${{ env.BUILD_MANIFEST_NAME }} - - # Build and package all the platform-agnostic(ish) things - build-global-artifacts: - needs: - - plan - - build-local-artifacts - runs-on: ubuntu-latest - container: - image: catthehacker/ubuntu:act-latest - volumes: - - /cache:/cache - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - BUILD_MANIFEST_NAME: target/distrib/global-dist-manifest.json - steps: - - uses: actions/checkout@v4 - with: - submodules: recursive - - name: Install Rust - uses: dtolnay/rust-toolchain@stable - # Restore dist binary from cache - - name: Restore cached dist - run: | - mkdir -p ~/.cargo/bin - cp $CI_CACHE/dist ~/.cargo/bin/dist - chmod +x ~/.cargo/bin/dist - # Replaced by /cache PVC - uncomment to revert to artifact storage - # - name: Install cached dist - # uses: actions/download-artifact@v3 - # with: - # name: cargo-dist-cache - # path: ~/.cargo/bin/ - # - run: chmod +x ~/.cargo/bin/dist - # Get all the local artifacts for the global tasks to use (for e.g. checksums) - - name: Restore local artifacts from cache - run: | - mkdir -p target/distrib/ - for dir in $CI_CACHE/artifacts-build-local-*; do - [ -d "$dir" ] && cp -r "$dir"/* target/distrib/ - done - # Replaced by /cache PVC - uncomment to revert to artifact storage - # - name: Fetch local artifacts - # uses: actions/download-artifact@v3 - # with: - # pattern: artifacts-* - # path: target/distrib/ - # merge-multiple: true - - id: cargo-dist - shell: bash - run: | - dist build ${{ needs.plan.outputs.tag-flag }} --output-format=json "--artifacts=global" > dist-manifest.json - echo "dist ran successfully" - - # Parse out what we just built and upload it to scratch storage - echo "paths<> "$GITHUB_OUTPUT" - jq --raw-output ".upload_files[]" dist-manifest.json >> "$GITHUB_OUTPUT" - echo "EOF" >> "$GITHUB_OUTPUT" - - cp dist-manifest.json "$BUILD_MANIFEST_NAME" - - name: Cache global artifacts - shell: bash - run: | - CACHE_DIR=$CI_CACHE/artifacts-build-global - mkdir -p "$CACHE_DIR" - if test -d "$CACHE_DIR"; then - echo "Cache directory ready: $CACHE_DIR" - else - echo "::error::Cache directory $CACHE_DIR is not available. Is the PVC mounted?" - exit 1 - fi - while IFS= read -r f; do - cp "$f" "$CACHE_DIR/" - done < <(jq --raw-output ".upload_files[]" dist-manifest.json) - cp "$BUILD_MANIFEST_NAME" "$CACHE_DIR/" - echo "Cached global artifacts:" - ls -lh "$CACHE_DIR/" - # Replaced by /cache PVC - uncomment to revert to artifact storage - # - name: "Upload artifacts" - # uses: actions/upload-artifact@v3 - # with: - # name: artifacts-build-global - # path: | - # ${{ steps.cargo-dist.outputs.paths }} - # ${{ env.BUILD_MANIFEST_NAME }} - - # Build and push Docker images (multi-arch) - docker: - name: Docker - needs: [test, lint, frontend, plugins] - # Only build Docker on main branch or release tags - if: ${{ github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/') }} - runs-on: ubuntu-latest - timeout-minutes: 240 - steps: - - uses: actions/checkout@v4 - - name: Set up QEMU - uses: docker/setup-qemu-action@v3 - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - name: Determine build info - id: info - run: | - if [[ "${{ github.ref }}" == refs/tags/* ]]; then - echo "is-release=true" >> "$GITHUB_OUTPUT" - else - echo "is-release=false" >> "$GITHUB_OUTPUT" - fi - - name: Registry FQDN Setup - id: registry - run: | - registry=${{ github.server_url }} - echo "registry=${registry##http*://}" >> "$GITHUB_OUTPUT" - - name: Login to Docker Registry - uses: docker/login-action@v3 - with: - registry: ${{ steps.registry.outputs.registry }} - username: ${{ github.actor }} - password: ${{ secrets.REGISTRY_TOKEN }} - - name: Docker meta (release) - if: ${{ steps.info.outputs.is-release == 'true' }} - id: meta-release - uses: docker/metadata-action@v5 - with: - images: ${{ steps.registry.outputs.registry }}/${{ github.repository }} - tags: | - type=semver,pattern={{version}} - type=semver,pattern={{major}}.{{minor}} - type=semver,pattern={{major}} - type=raw,value=latest - type=sha,prefix=sha- - - name: Docker meta (main) - if: ${{ steps.info.outputs.is-release == 'false' }} - id: meta-main - uses: docker/metadata-action@v5 - with: - images: ${{ steps.registry.outputs.registry }}/${{ github.repository }} - tags: | - type=raw,value=main - type=raw,value=latest - type=sha,prefix=sha- - - name: Build and push Docker image - uses: docker/build-push-action@v6 - with: - context: . - file: Dockerfile.cross - platforms: linux/amd64,linux/arm64 - push: true - tags: ${{ steps.info.outputs.is-release == 'true' && steps.meta-release.outputs.tags || steps.meta-main.outputs.tags }} - labels: ${{ steps.info.outputs.is-release == 'true' && steps.meta-release.outputs.labels || steps.meta-main.outputs.labels }} - cache-from: type=registry,ref=${{ steps.registry.outputs.registry }}/${{ github.repository }}:buildcache - cache-to: type=registry,ref=${{ steps.registry.outputs.registry }}/${{ github.repository }}:buildcache,mode=max - - # Create Release (only for releases) - host: - name: Release - needs: - - plan - - build-local-artifacts - - build-global-artifacts - - docker - # Only run if we're "publishing" a release, and only if plan, local, global didn't fail (skipped is fine) - if: ${{ always() && needs.plan.result == 'success' && needs.plan.outputs.is-release == 'true' && (needs.build-global-artifacts.result == 'skipped' || needs.build-global-artifacts.result == 'success') && (needs.build-local-artifacts.result == 'skipped' || needs.build-local-artifacts.result == 'success') }} - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - runs-on: ubuntu-latest - container: - image: catthehacker/ubuntu:act-latest - volumes: - - /cache:/cache - outputs: - val: ${{ steps.host.outputs.manifest }} - steps: - - uses: actions/checkout@v4 - with: - submodules: recursive - - name: Install Rust - uses: dtolnay/rust-toolchain@stable - # Restore dist binary from cache - - name: Restore cached dist - run: | - mkdir -p ~/.cargo/bin - cp $CI_CACHE/dist ~/.cargo/bin/dist - chmod +x ~/.cargo/bin/dist - # Replaced by /cache PVC - uncomment to revert to artifact storage - # - name: Install cached dist - # uses: actions/download-artifact@v3 - # with: - # name: cargo-dist-cache - # path: ~/.cargo/bin/ - # - run: chmod +x ~/.cargo/bin/dist - # Fetch artifacts from cache - - name: Restore artifacts from cache - run: | - mkdir -p target/distrib/ - for dir in $CI_CACHE/artifacts-build-*; do - [ -d "$dir" ] && cp -r "$dir"/* target/distrib/ - done - # Replaced by /cache PVC - uncomment to revert to artifact storage - # - name: Fetch artifacts - # uses: actions/download-artifact@v3 - # with: - # pattern: artifacts-* - # path: target/distrib/ - # merge-multiple: true - - id: host - shell: bash - run: | - dist host ${{ needs.plan.outputs.tag-flag }} --steps=upload --steps=release --output-format=json > dist-manifest.json - echo "artifacts uploaded and released successfully" - cat dist-manifest.json - echo "manifest=$(jq -c "." dist-manifest.json)" >> "$GITHUB_OUTPUT" - # Replaced by /cache PVC - uncomment to revert to artifact storage - # - name: "Upload dist-manifest.json" - # uses: actions/upload-artifact@v3 - # with: - # name: artifacts-dist-manifest - # path: dist-manifest.json - # Create a Release while uploading all files from cache - - name: Restore release artifacts from cache - run: | - mkdir -p artifacts/ - for dir in $CI_CACHE/artifacts-build-*; do - [ -d "$dir" ] && cp -r "$dir"/* artifacts/ - done - # Remove the granular manifests - rm -f artifacts/*-dist-manifest.json - # Replaced by /cache PVC - uncomment to revert to artifact storage - # - name: "Download Artifacts" - # uses: actions/download-artifact@v3 - # with: - # pattern: artifacts-* - # path: artifacts - # merge-multiple: true - # - name: Cleanup - # run: | - # # Remove the granular manifests - # rm -f artifacts/*-dist-manifest.json - - name: Publish packages to Gitea registry - env: - GITHUB_TOKEN: ${{ secrets.PACKAGE_TOKEN || secrets.GITHUB_TOKEN }} - run: | - cd artifacts - version="${{ needs.plan.outputs.tag }}" - - for file in *.tar.gz *.tar.xz *.zip; do - if [ -f "$file" ]; then - echo "Publishing $file..." - - # Determine content type - if [[ "$file" == *.zip ]]; then - content_type="application/zip" - elif [[ "$file" == *.tar.xz ]]; then - content_type="application/x-xz" - else - content_type="application/gzip" - fi - - curl -s -X PUT \ - -H "Authorization: token $GITHUB_TOKEN" \ - -H "Content-Type: $content_type" \ - --data-binary @"$file" \ - "${{ github.server_url }}/api/packages/${{ github.repository_owner }}/generic/codex/$version/$file" || echo "Failed to upload $file" - fi - done - - echo "All artifacts published to version: $version" - - name: Create Release - env: - PRERELEASE_FLAG: "${{ fromJson(steps.host.outputs.manifest).announcement_is_prerelease && '--prerelease' || '' }}" - ANNOUNCEMENT_TITLE: "${{ fromJson(steps.host.outputs.manifest).announcement_title }}" - ANNOUNCEMENT_BODY: "${{ fromJson(steps.host.outputs.manifest).announcement_github_body }}" - RELEASE_COMMIT: "${{ github.sha }}" - GITHUB_TOKEN: ${{ secrets.PACKAGE_TOKEN || secrets.GITHUB_TOKEN }} - run: | - # Install gh CLI if not available - if ! command -v gh &> /dev/null; then - apt-get update && apt-get install -y gh - fi - - # Write and read notes from a file to avoid quoting breaking things - echo "$ANNOUNCEMENT_BODY" > $RUNNER_TEMP/notes.txt - - gh release create "${{ needs.plan.outputs.tag }}" --target "$RELEASE_COMMIT" $PRERELEASE_FLAG --title "$ANNOUNCEMENT_TITLE" --notes-file "$RUNNER_TEMP/notes.txt" artifacts/* - - # Publish SDK to npm (only for releases) - publish-sdk: - name: Publish SDK - needs: - - plan - - host - if: ${{ always() && needs.host.result == 'success' }} - runs-on: ubuntu-latest - container: - image: catthehacker/ubuntu:act-latest - steps: - - uses: actions/checkout@v4 - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: "22" - registry-url: "https://registry.npmjs.org" - # Note: We remove package-lock.json and use npm install instead of npm ci - # because Biome updates frequently and we want to use the latest compatible version - - name: Install dependencies and build - working-directory: plugins/sdk-typescript - run: | - rm -rf node_modules package-lock.json - npm install - npm run build - - name: Publish to npm - working-directory: plugins/sdk-typescript - run: npm publish --access public - env: - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} - - # Publish plugins to npm (only for releases, after SDK is published) - publish-plugins: - name: Publish Plugins - needs: - - plan - - publish-sdk - if: ${{ always() && needs.publish-sdk.result == 'success' }} - runs-on: ubuntu-latest - container: - image: catthehacker/ubuntu:act-latest - strategy: - fail-fast: false - matrix: - plugin: - - metadata-echo - - metadata-mangabaka - - metadata-openlibrary - - recommendations-anilist - - sync-anilist - steps: - - uses: actions/checkout@v4 - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: "22" - registry-url: "https://registry.npmjs.org" - # Update SDK dependency from file: to published npm version before install - - name: Update SDK dependency for publish - working-directory: plugins/${{ matrix.plugin }} - run: | - SDK_VERSION=$(jq -r .version ../sdk-typescript/package.json) - echo "Updating @ashdev/codex-plugin-sdk dependency to ^$SDK_VERSION" - jq --arg v "^$SDK_VERSION" '.dependencies["@ashdev/codex-plugin-sdk"] = $v' package.json > tmp.json - mv tmp.json package.json - cat package.json | grep -A2 '"dependencies"' - - name: Install plugin dependencies - working-directory: plugins/${{ matrix.plugin }} - run: | - rm -rf node_modules package-lock.json - npm install - - name: Build plugin - working-directory: plugins/${{ matrix.plugin }} - run: npm run build - - name: Publish to npm - working-directory: plugins/${{ matrix.plugin }} - run: npm publish --access public - env: - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} - - announce: - needs: - - plan - - host - - publish-sdk - - publish-plugins - # use "always() && ..." to allow us to wait for all publish jobs while - # still allowing individual publish jobs to skip themselves (for prereleases). - # "host" however must run to completion, no skipping allowed! - if: ${{ always() && needs.host.result == 'success' }} - runs-on: ubuntu-latest - container: - image: catthehacker/ubuntu:act-latest - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - steps: - - uses: actions/checkout@v4 - with: - submodules: recursive - - # Clean up intermediate cache that is no longer needed - cleanup: - name: Cleanup - needs: - - plan - - build-local-artifacts - - build-global-artifacts - - host - - docker - - announce - if: always() - runs-on: ubuntu-latest - container: - image: catthehacker/ubuntu:act-latest - volumes: - - /cache:/cache - steps: - - name: Clean up CI cache - run: | - echo "Removing current run cache: $CI_CACHE" - rm -rf $CI_CACHE - # Remove orphaned cache directories older than 24 hours (from cancelled/crashed runs) - echo "Cleaning orphaned cache directories older than 24h..." - find /cache/ci -mindepth 1 -maxdepth 1 -type d -mmin +1440 -exec echo "Removing stale: {}" \; -exec rm -rf {} \; diff --git a/.gitea/workflows/ci.yaml b/.gitea/workflows/ci.yaml deleted file mode 100644 index cfcaf38f..00000000 --- a/.gitea/workflows/ci.yaml +++ /dev/null @@ -1,271 +0,0 @@ -# CI workflow for Pull Requests (Gitea) -# Runs tests, linting, frontend checks, and Docker build with PR tag - -name: CI - -permissions: - contents: read - packages: write - -on: - pull_request: - branches: - - main - -# Cancel in-progress runs for same PR -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -# Shared cache directory on mount available to runners -# Each workflow run gets its own isolated directory under /cache/ci -env: - CI_CACHE: /cache/ci/${{ github.run_id }} - -jobs: - # Build test binaries and create nextest archive - build-tests: - name: Build Tests - runs-on: ubuntu-latest - container: - image: catthehacker/ubuntu:act-latest - volumes: - - /cache:/cache - timeout-minutes: 60 - env: - CARGO_INCREMENTAL: "0" - # Limit parallel rustc jobs to reduce peak memory (OOMKill prevention) - CARGO_BUILD_JOBS: "1" - # Strip debug info to reduce memory usage during compilation and linking - CARGO_PROFILE_TEST_DEBUG: "0" - steps: - - name: Install base dependencies - run: | - apt-get update - apt-get install -y build-essential pkg-config clang mold - - uses: actions/checkout@v4 - - name: Install Rust - uses: dtolnay/rust-toolchain@stable - - name: Install cargo-nextest - uses: taiki-e/install-action@nextest - - name: Show memory info - run: | - echo "=== Memory Info ===" - free -h || cat /proc/meminfo || echo "Cannot determine memory" - echo "=== CPU Info ===" - nproc || echo "Cannot determine CPU count" - - name: Prepare cache directory - run: | - mkdir -p $CI_CACHE - if test -d $CI_CACHE; then - echo "CI_CACHE directory ready: $CI_CACHE" - ls -la $(dirname $CI_CACHE) - else - echo "::error::CI_CACHE directory $CI_CACHE is not available. Is the PVC mounted?" - exit 1 - fi - - name: Build and archive tests - run: | - cargo nextest archive --features rar --archive-file $CI_CACHE/nextest-archive.tar.zst - echo "=== Post-build memory ===" - free -h || cat /proc/meminfo || true - echo "=== Verifying archive ===" - ls -lh $CI_CACHE/nextest-archive.tar.zst || { echo "::error::Failed to write nextest archive to cache"; exit 1; } - # Replaced by /cache PVC - uncomment to revert to artifact storage - # - name: Upload test archive - # uses: actions/upload-artifact@v3 - # with: - # name: nextest-archive - # path: nextest-archive.tar.zst - # retention-days: 1 - - # Run tests in parallel partitions - test: - name: Test (${{ matrix.partition }}/5) - needs: build-tests - runs-on: ubuntu-latest - container: - image: catthehacker/ubuntu:act-latest - volumes: - - /cache:/cache - timeout-minutes: 60 - strategy: - fail-fast: false - matrix: - partition: [1, 2, 3, 4, 5] - steps: - - uses: actions/checkout@v4 - - name: Install Rust - uses: dtolnay/rust-toolchain@stable - - name: Install cargo-nextest - uses: taiki-e/install-action@nextest - # Replaced by /cache PVC - uncomment to revert to artifact storage - # - name: Download test archive - # uses: actions/download-artifact@v3 - # with: - # name: nextest-archive - - name: Run tests (partition ${{ matrix.partition }}/5) - run: cargo nextest run --archive-file $CI_CACHE/nextest-archive.tar.zst --partition hash:${{ matrix.partition }}/5 - - # Run linting checks - lint: - name: Lint - runs-on: ubuntu-latest - container: - image: catthehacker/ubuntu:act-latest - timeout-minutes: 30 - steps: - - name: Install base dependencies - run: | - apt-get update - apt-get install -y build-essential pkg-config clang mold - - uses: actions/checkout@v4 - - name: Install Rust - uses: dtolnay/rust-toolchain@stable - with: - components: rustfmt, clippy - - name: Format check - run: cargo fmt -- --check - - name: Clippy check - run: cargo clippy --features rar -- -D warnings - - # Run frontend tests and build - frontend: - name: Frontend - runs-on: ubuntu-latest - container: - image: catthehacker/ubuntu:act-latest - timeout-minutes: 30 - steps: - - uses: actions/checkout@v4 - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: "22" - - name: Install dependencies - working-directory: web - run: | - rm -rf node_modules package-lock.json - npm install - - name: Lint check - working-directory: web - run: npm run lint - - name: Run tests - working-directory: web - run: npm run test:run - - name: Build frontend - working-directory: web - run: npm run build - - # Run plugin checks (lint, typecheck, tests) - plugins: - name: Plugins (${{ matrix.plugin }}) - runs-on: ubuntu-latest - container: - image: catthehacker/ubuntu:act-latest - timeout-minutes: 30 - strategy: - fail-fast: false - matrix: - plugin: - - sdk-typescript - - metadata-echo - - metadata-mangabaka - - metadata-openlibrary - - recommendations-anilist - - sync-anilist - steps: - - uses: actions/checkout@v4 - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: "22" - # SDK must be built first for other plugins to use - - name: Build SDK (dependency) - if: matrix.plugin != 'sdk-typescript' - working-directory: plugins/sdk-typescript - run: | - rm -rf node_modules package-lock.json - npm install - npm run build - - name: Install dependencies - working-directory: plugins/${{ matrix.plugin }} - run: | - rm -rf node_modules package-lock.json - npm install - - name: Lint - working-directory: plugins/${{ matrix.plugin }} - run: npm run lint - - name: Typecheck - working-directory: plugins/${{ matrix.plugin }} - run: npx tsc --noEmit - - name: Test - working-directory: plugins/${{ matrix.plugin }} - run: npm run test - - name: Build - working-directory: plugins/${{ matrix.plugin }} - run: npm run build - - # Build and push Docker image with PR tag - docker: - name: Docker - needs: [test, lint, frontend, plugins] - runs-on: ubuntu-latest - timeout-minutes: 240 - steps: - - uses: actions/checkout@v4 - - name: Set up QEMU - uses: docker/setup-qemu-action@v3 - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - name: Registry FQDN Setup - id: registry - run: | - registry=${{ github.server_url }} - echo "registry=${registry##http*://}" >> "$GITHUB_OUTPUT" - - name: Login to Docker Registry - uses: docker/login-action@v3 - with: - registry: ${{ steps.registry.outputs.registry }} - username: ${{ github.actor }} - password: ${{ secrets.REGISTRY_TOKEN }} - - name: Docker meta - id: meta - uses: docker/metadata-action@v5 - with: - images: ${{ steps.registry.outputs.registry }}/${{ github.repository }} - tags: | - type=ref,event=pr - type=sha,prefix=pr-${{ github.event.pull_request.number }}-sha- - - name: Build and push Docker image - uses: docker/build-push-action@v6 - with: - context: . - file: Dockerfile.cross - platforms: linux/amd64,linux/arm64 - push: true - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} - cache-from: type=registry,ref=${{ steps.registry.outputs.registry }}/${{ github.repository }}:buildcache - cache-to: type=registry,ref=${{ steps.registry.outputs.registry }}/${{ github.repository }}:buildcache,mode=max - - # Clean up intermediate cache that is no longer needed - cleanup: - name: Cleanup - needs: - - test - - docker - if: always() - runs-on: ubuntu-latest - container: - image: catthehacker/ubuntu:act-latest - volumes: - - /cache:/cache - steps: - - name: Clean up CI cache - run: | - echo "Removing current run cache: $CI_CACHE" - rm -rf $CI_CACHE - # Remove orphaned cache directories older than 24 hours (from cancelled/crashed runs) - echo "Cleaning orphaned cache directories older than 24h..." - find /cache/ci -mindepth 1 -maxdepth 1 -type d -mmin +1440 -exec echo "Removing stale: {}" \; -exec rm -rf {} \; diff --git a/.gitea/workflows/docs.yaml b/.gitea/workflows/docs.yaml deleted file mode 100644 index 40cfc694..00000000 --- a/.gitea/workflows/docs.yaml +++ /dev/null @@ -1,66 +0,0 @@ -name: Build and Deploy Docs - -on: - push: - branches: - - main - paths: - - ".gitea/workflows/docs.yaml" - - "docs/**" - - "web/openapi.json" - pull_request: - branches: - - main - paths: - - ".gitea/workflows/docs.yaml" - - "docs/**" - - "web/openapi.json" - workflow_dispatch: # Allow manual trigger - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -jobs: - # Build and push Docker image for docs (push to main only) - docker-docs: - if: github.event_name == 'push' && github.ref == 'refs/heads/main' - runs-on: ubuntu-latest - container: - image: catthehacker/ubuntu:act-latest - timeout-minutes: 60 - - steps: - - uses: actions/checkout@v4 - - - name: Registry FQDN Setup - id: registry - run: | - registry=${{ github.server_url }} - echo "registry=${registry##http*://}" >> "$GITHUB_OUTPUT" - - - name: Login to Docker Registry - uses: docker/login-action@v3 - with: - registry: ${{ steps.registry.outputs.registry }} - username: ${{ github.actor }} - password: ${{ secrets.REGISTRY_TOKEN }} - - - name: Docker meta - id: meta - uses: docker/metadata-action@v5 - with: - images: | - ${{ steps.registry.outputs.registry }}/${{ github.repository }}-docs - tags: | - type=raw,value=latest - type=sha - - - name: Build and push docs Docker image - uses: docker/build-push-action@v6 - with: - context: . - file: docs/Dockerfile - push: true - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 00000000..290b5d31 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,98 @@ +name: Bug Report +description: Report a bug or unexpected behavior +labels: ["bug"] +body: + - type: markdown + attributes: + value: | + Thanks for taking the time to report a bug. Please fill out the sections below to help us reproduce and fix the issue. + + - type: input + id: version + attributes: + label: Codex Version + description: Output of `codex --version` or the Docker image tag you're using + placeholder: "1.13.0" + validations: + required: true + + - type: dropdown + id: deployment + attributes: + label: Deployment Method + options: + - Docker + - Docker Compose + - Binary (standalone) + - Built from source + validations: + required: true + + - type: dropdown + id: database + attributes: + label: Database + options: + - SQLite + - PostgreSQL + validations: + required: true + + - type: textarea + id: description + attributes: + label: Description + description: A clear and concise description of the bug + validations: + required: true + + - type: textarea + id: steps + attributes: + label: Steps to Reproduce + description: Steps to reproduce the behavior + placeholder: | + 1. Go to '...' + 2. Click on '...' + 3. See error + validations: + required: true + + - type: textarea + id: expected + attributes: + label: Expected Behavior + description: What you expected to happen + validations: + required: true + + - type: textarea + id: actual + attributes: + label: Actual Behavior + description: What actually happened + validations: + required: true + + - type: textarea + id: logs + attributes: + label: Relevant Logs + description: Paste any relevant log output (set `RUST_LOG=debug` for more detail) + render: shell + + - type: textarea + id: environment + attributes: + label: Environment + description: Any additional environment details + placeholder: | + OS: Ubuntu 24.04 + Architecture: x86_64 + Browser: Firefox 128 (if frontend issue) + + - type: textarea + id: context + attributes: + label: Additional Context + description: Any other context, screenshots, or file samples that might help diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 00000000..20beab4e --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,5 @@ +blank_issues_enabled: true +contact_links: + - name: Documentation + url: https://codex.4sh.dev + about: Check the documentation before opening an issue diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 00000000..15cafee7 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,55 @@ +name: Feature Request +description: Suggest a new feature or improvement +labels: ["feature"] +body: + - type: markdown + attributes: + value: | + Thanks for suggesting a feature! Please describe what you'd like and why it would be useful. + + - type: dropdown + id: area + attributes: + label: Area + description: Which part of Codex does this relate to? + options: + - Backend / API + - Frontend / UI + - Library scanning + - File format support + - OPDS + - Docker / Deployment + - Documentation + - Plugins / SDK + - Other + validations: + required: true + + - type: textarea + id: problem + attributes: + label: Problem or Motivation + description: What problem does this solve, or what use case does it address? + placeholder: "I'm always frustrated when ..." + validations: + required: true + + - type: textarea + id: solution + attributes: + label: Proposed Solution + description: Describe how you'd like this to work + validations: + required: true + + - type: textarea + id: alternatives + attributes: + label: Alternatives Considered + description: Any alternative solutions or workarounds you've considered + + - type: textarea + id: context + attributes: + label: Additional Context + description: Any other context, mockups, or references that might help diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 35b978e6..5d67413f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -5,22 +5,6 @@ # Triggers: # - Push to main: test, lint, build artifacts, push Docker with 'main' tag # - Push version tag: test, lint, build artifacts, push Docker with version tags, create GitHub release -# -# Note: PRs are handled by ci.yml (test, lint, frontend, Docker build validation) -# -# ============================================================================= -# SELF-HOSTED RUNNER MODIFICATIONS -# ============================================================================= -# The following changes were made to support self-hosted runners (arc-runner-codex) -# instead of GitHub-hosted runners. To revert to paid GitHub runners: -# -# 1. Replace all `runs-on: arc-runner-codex` with `runs-on: ubuntu-latest` or `ubuntu-22.04` -# 2. Remove all "Install build dependencies" steps (GitHub runners have these pre-installed) -# 3. Remove the "Install Rust" step from the `plan` job (only needed for self-hosted) -# 4. Remove the "Install build dependencies (Linux)" step from `build-local-artifacts` -# 5. Update Cargo.toml: restore full targets list and remove custom runner configs -# (see comments in Cargo.toml under [workspace.metadata.dist]) -# ============================================================================= name: Build @@ -42,21 +26,11 @@ concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true -# Shared cache directory on PVC mounted by ARC runners -# Each workflow run gets its own isolated directory under /ci-cache -env: - CI_CACHE: /ci-cache/${{ github.run_id }} - jobs: # Build test binaries and create nextest archive build-tests: name: Build Tests - # SELF-HOSTED: Change to ubuntu-latest for GitHub runners and remove container - runs-on: arc-runner-codex - container: - image: ubuntu:22.04 - volumes: - - /ci-cache:/ci-cache + runs-on: ubuntu-latest timeout-minutes: 60 env: SCCACHE_GHA_ENABLED: "true" @@ -64,101 +38,56 @@ jobs: RUSTC_WRAPPER: sccache CARGO_INCREMENTAL: "0" steps: - - name: Install base dependencies - uses: nick-fields/retry@v3 - with: - timeout_minutes: 10 - max_attempts: 3 - command: | - apt-get update - apt-get install -y git curl build-essential pkg-config clang mold - uses: actions/checkout@v4 - - name: Install Rust - uses: dtolnay/rust-toolchain@stable + - name: Install mold linker + run: sudo apt-get update && sudo apt-get install -y mold - name: Install cargo-nextest uses: taiki-e/install-action@nextest - name: Setup sccache uses: mozilla-actions/sccache-action@v0.0.9 - - name: Prepare cache directory - run: | - mkdir -p $CI_CACHE - if test -d $CI_CACHE; then - echo "CI_CACHE directory ready: $CI_CACHE" - ls -la $(dirname $CI_CACHE) - else - echo "::error::CI_CACHE directory $CI_CACHE is not available. Is the PVC mounted?" - exit 1 - fi - name: Build and archive tests - run: | - cargo nextest archive --features rar --archive-file $CI_CACHE/nextest-archive.tar.zst - ls -lh $CI_CACHE/nextest-archive.tar.zst || { echo "::error::Failed to write nextest archive to cache"; exit 1; } - # Replaced by /ci-cache PVC - uncomment to revert to GitHub artifact storage - # - name: Upload test archive - # uses: actions/upload-artifact@v4 - # with: - # name: nextest-archive - # path: nextest-archive.tar.zst - # retention-days: 1 + run: cargo nextest archive --features rar --archive-file nextest-archive.tar.zst + - name: Upload test archive + uses: actions/upload-artifact@v4 + with: + name: nextest-archive + path: nextest-archive.tar.zst + retention-days: 1 # Run tests in parallel partitions test: name: Test (${{ matrix.partition }}/5) needs: build-tests - # SELF-HOSTED: Change to ubuntu-latest for GitHub runners and remove container - runs-on: arc-runner-codex - container: - image: ubuntu:22.04 - volumes: - - /ci-cache:/ci-cache + runs-on: ubuntu-latest timeout-minutes: 60 strategy: fail-fast: false matrix: partition: [1, 2, 3, 4, 5] steps: - - name: Install base dependencies - uses: nick-fields/retry@v3 - with: - timeout_minutes: 10 - max_attempts: 3 - command: | - apt-get update - apt-get install -y git curl - uses: actions/checkout@v4 - - name: Install Rust - uses: dtolnay/rust-toolchain@stable - name: Install cargo-nextest uses: taiki-e/install-action@nextest - # Replaced by /ci-cache PVC - uncomment to revert to GitHub artifact storage - # - name: Download test archive - # uses: actions/download-artifact@v4 - # with: - # name: nextest-archive + - name: Download test archive + uses: actions/download-artifact@v4 + with: + name: nextest-archive - name: Run tests (partition ${{ matrix.partition }}/5) - run: cargo nextest run --archive-file $CI_CACHE/nextest-archive.tar.zst --partition hash:${{ matrix.partition }}/5 + run: cargo nextest run --archive-file nextest-archive.tar.zst --partition hash:${{ matrix.partition }}/5 # Run linting checks lint: name: Lint - # SELF-HOSTED: Change to ubuntu-latest for GitHub runners and remove container - runs-on: arc-runner-codex - container: ubuntu:22.04 + runs-on: ubuntu-latest timeout-minutes: 30 env: SCCACHE_GHA_ENABLED: "true" SCCACHE_GHA_VERSION: lint RUSTC_WRAPPER: sccache steps: - - name: Install base dependencies - uses: nick-fields/retry@v3 - with: - timeout_minutes: 10 - max_attempts: 3 - command: | - apt-get update - apt-get install -y git curl build-essential pkg-config clang mold - uses: actions/checkout@v4 + - name: Install mold linker + run: sudo apt-get update && sudo apt-get install -y mold - name: Install Rust uses: dtolnay/rust-toolchain@stable with: @@ -173,22 +102,9 @@ jobs: # Run frontend tests and build frontend: name: Frontend - # SELF-HOSTED: Change to ubuntu-latest for GitHub runners and remove container - runs-on: arc-runner-codex - container: - image: ubuntu:22.04 - volumes: - - /ci-cache:/ci-cache + runs-on: ubuntu-latest timeout-minutes: 30 steps: - - name: Install base dependencies - uses: nick-fields/retry@v3 - with: - timeout_minutes: 10 - max_attempts: 3 - command: | - apt-get update - apt-get install -y git curl - uses: actions/checkout@v4 - name: Setup Node.js uses: actions/setup-node@v4 @@ -213,34 +129,17 @@ jobs: - name: Build frontend working-directory: web run: npm run build - - name: Prepare cache directory - run: | - mkdir -p $CI_CACHE - if test -d $CI_CACHE; then - echo "CI_CACHE directory ready: $CI_CACHE" - ls -la $(dirname $CI_CACHE) - else - echo "::error::CI_CACHE directory $CI_CACHE is not available. Is the PVC mounted?" - exit 1 - fi - - name: Cache frontend build - run: | - cp -r web/dist $CI_CACHE/frontend-dist - ls -lh $CI_CACHE/frontend-dist/ || { echo "::error::Failed to cache frontend build"; exit 1; } - # Replaced by /ci-cache PVC - uncomment to revert to GitHub artifact storage - # - name: Upload frontend build - # uses: actions/upload-artifact@v4 - # with: - # name: frontend-dist - # path: web/dist - # retention-days: 1 + - name: Upload frontend build + uses: actions/upload-artifact@v4 + with: + name: frontend-dist + path: web/dist + retention-days: 1 # Run plugin checks (lint, typecheck, tests) plugins: name: Plugins (${{ matrix.plugin }}) - # SELF-HOSTED: Change to ubuntu-latest for GitHub runners and remove container - runs-on: arc-runner-codex - container: ubuntu:22.04 + runs-on: ubuntu-latest timeout-minutes: 30 strategy: fail-fast: false @@ -253,14 +152,6 @@ jobs: - recommendations-anilist - sync-anilist steps: - - name: Install base dependencies - uses: nick-fields/retry@v3 - with: - timeout_minutes: 10 - max_attempts: 3 - command: | - apt-get update - apt-get install -y git curl - uses: actions/checkout@v4 - name: Setup Node.js uses: actions/setup-node@v4 @@ -296,12 +187,7 @@ jobs: plan: name: Plan needs: [test, lint, frontend, plugins] - # SELF-HOSTED: Change to ubuntu-22.04 for GitHub runners and remove container - runs-on: arc-runner-codex - container: - image: ubuntu:22.04 - volumes: - - /ci-cache:/ci-cache + runs-on: ubuntu-22.04 outputs: val: ${{ steps.plan.outputs.manifest }} tag: ${{ steps.info.outputs.tag }} @@ -312,19 +198,9 @@ jobs: env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} steps: - - name: Install base dependencies - uses: nick-fields/retry@v3 - with: - timeout_minutes: 10 - max_attempts: 3 - command: | - apt-get update - apt-get install -y git curl xz-utils jq bash - uses: actions/checkout@v4 with: persist-credentials: false - - name: Install Rust - uses: dtolnay/rust-toolchain@stable - name: Determine build info id: info shell: bash @@ -355,27 +231,12 @@ jobs: - name: Install dist shell: bash run: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/v0.30.3/cargo-dist-installer.sh | sh" - - name: Prepare cache directory - run: | - mkdir -p $CI_CACHE - if test -d $CI_CACHE; then - echo "CI_CACHE directory ready: $CI_CACHE" - ls -la $(dirname $CI_CACHE) - else - echo "::error::CI_CACHE directory $CI_CACHE is not available. Is the PVC mounted?" - exit 1 - fi - - name: Cache dist binary - run: | - cp ~/.cargo/bin/dist $CI_CACHE/dist - ls -lh $CI_CACHE/dist || { echo "::error::Failed to cache dist binary"; exit 1; } - # Replaced by /ci-cache PVC - uncomment to revert to GitHub artifact storage - # - name: Cache dist - # uses: actions/upload-artifact@v4 - # with: - # name: cargo-dist-cache - # path: ~/.cargo/bin/dist - # retention-days: 1 + - name: Upload cached dist + uses: actions/upload-artifact@v4 + with: + name: cargo-dist-cache + path: ~/.cargo/bin/dist + retention-days: 1 - name: Run dist plan id: plan shell: bash @@ -388,17 +249,12 @@ jobs: echo "dist ran successfully" cat plan-dist-manifest.json echo "manifest=$(jq -c "." plan-dist-manifest.json)" >> "$GITHUB_OUTPUT" - - name: Cache dist-manifest.json - run: | - cp plan-dist-manifest.json $CI_CACHE/plan-dist-manifest.json - ls -lh $CI_CACHE/plan-dist-manifest.json || { echo "::error::Failed to cache dist manifest"; exit 1; } - # Replaced by /ci-cache PVC - uncomment to revert to GitHub artifact storage - # - name: "Upload dist-manifest.json" - # uses: actions/upload-artifact@v4 - # with: - # name: artifacts-plan-dist-manifest - # path: plan-dist-manifest.json - # retention-days: 1 + - name: Upload dist-manifest.json + uses: actions/upload-artifact@v4 + with: + name: artifacts-plan-dist-manifest + path: plan-dist-manifest.json + retention-days: 1 # Build and packages all the platform-specific things build-local-artifacts: @@ -421,11 +277,6 @@ jobs: # - N "local" tasks that build each platform's binaries and platform-specific installers matrix: ${{ fromJson(needs.plan.outputs.val).ci.github.artifacts_matrix }} runs-on: ${{ matrix.runner }} - # SELF-HOSTED: Use ubuntu:22.04 container for consistent environment; remove for GitHub runners - container: - image: ubuntu:22.04 - volumes: - - /ci-cache:/ci-cache env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} BUILD_MANIFEST_NAME: target/distrib/${{ join(matrix.targets, '-') }}-dist-manifest.json @@ -434,58 +285,33 @@ jobs: "contents": "read" "id-token": "write" steps: - - name: Install base dependencies - uses: nick-fields/retry@v3 - with: - timeout_minutes: 10 - max_attempts: 3 - command: | - apt-get update - apt-get install -y git curl xz-utils build-essential pkg-config lsb-release clang mold - uses: actions/checkout@v4 with: persist-credentials: false - - name: Install Rust - uses: dtolnay/rust-toolchain@stable - name: Install dist run: ${{ matrix.install_dist.run }} - # Restore cached artifacts from PVC - - name: Restore plan manifest from cache - run: | - mkdir -p target/distrib/ - cp $CI_CACHE/plan-dist-manifest.json target/distrib/ - - name: Restore frontend dist from cache - run: | - mkdir -p web/dist/ - cp -r $CI_CACHE/frontend-dist/* web/dist/ - # Replaced by /ci-cache PVC - uncomment to revert to GitHub artifact storage - # - name: Fetch local artifacts - # uses: actions/download-artifact@v4 - # with: - # pattern: artifacts-* - # path: target/distrib/ - # merge-multiple: true - # - name: Download frontend dist - # uses: actions/download-artifact@v4 - # with: - # name: frontend-dist - # path: web/dist/ + - name: Fetch local artifacts + uses: actions/download-artifact@v4 + with: + pattern: artifacts-* + path: target/distrib/ + merge-multiple: true + - name: Download frontend dist + uses: actions/download-artifact@v4 + with: + name: frontend-dist + path: web/dist/ - name: Build artifacts run: | # Actually do builds and make zips and whatnot dist build ${{ needs.plan.outputs.tag-flag }} --print=linkage --output-format=json ${{ matrix.dist_args }} > dist-manifest.json echo "dist ran successfully" - # TODO: Enable attestation when repository is public - # See: https://docs.github.com/rest/repos/attestations#create-an-attestation - # - name: Attest - # uses: actions/attest-build-provenance@v2 - # with: - # subject-path: "target/distrib/*${{ join(matrix.targets, ', ') }}*" + - name: Attest + uses: actions/attest-build-provenance@v2 + with: + subject-path: "target/distrib/*${{ join(matrix.targets, ', ') }}*" - id: cargo-dist name: Post-build - # We force bash here just because github makes it really hard to get values up - # to "real" actions without writing to env-vars, and writing to env-vars has - # inconsistent syntax between shell and powershell. shell: bash run: | # Parse out what we just built and upload it to scratch storage @@ -494,95 +320,44 @@ jobs: echo "EOF" >> "$GITHUB_OUTPUT" cp dist-manifest.json "$BUILD_MANIFEST_NAME" - - name: Cache build artifacts - shell: bash - run: | - CACHE_DIR=$CI_CACHE/artifacts-build-local-${{ join(matrix.targets, '_') }} - mkdir -p "$CACHE_DIR" - if test -d "$CACHE_DIR"; then - echo "Cache directory ready: $CACHE_DIR" - else - echo "::error::Cache directory $CACHE_DIR is not available. Is the PVC mounted?" - exit 1 - fi - # Copy upload files listed by dist - while IFS= read -r f; do - cp "$f" "$CACHE_DIR/" - done <<< "${{ steps.cargo-dist.outputs.paths }}" - cp "$BUILD_MANIFEST_NAME" "$CACHE_DIR/" - echo "Cached build artifacts:" - ls -lh "$CACHE_DIR/" - # Replaced by /ci-cache PVC - uncomment to revert to GitHub artifact storage - # - name: "Upload artifacts" - # uses: actions/upload-artifact@v4 - # with: - # name: artifacts-build-local-${{ join(matrix.targets, '_') }} - # path: | - # ${{ steps.cargo-dist.outputs.paths }} - # ${{ env.BUILD_MANIFEST_NAME }} - # retention-days: 5 + - name: Upload artifacts + uses: actions/upload-artifact@v4 + with: + name: artifacts-build-local-${{ join(matrix.targets, '_') }} + path: | + ${{ steps.cargo-dist.outputs.paths }} + ${{ env.BUILD_MANIFEST_NAME }} + retention-days: 5 # Build and package all the platform-agnostic(ish) things build-global-artifacts: needs: - plan - build-local-artifacts - # SELF-HOSTED: Change to ubuntu-22.04 for GitHub runners and remove container - runs-on: arc-runner-codex - container: - image: ubuntu:22.04 - volumes: - - /ci-cache:/ci-cache + runs-on: ubuntu-22.04 env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} BUILD_MANIFEST_NAME: target/distrib/global-dist-manifest.json steps: - - name: Install base dependencies - uses: nick-fields/retry@v3 - with: - timeout_minutes: 10 - max_attempts: 3 - command: | - apt-get update - apt-get install -y git curl jq - uses: actions/checkout@v4 with: persist-credentials: false - - name: Mark repository as safe - run: git config --global --add safe.directory "$GITHUB_WORKSPACE" - - name: Install Rust - uses: dtolnay/rust-toolchain@stable - # Restore dist binary from PVC cache - - name: Restore cached dist + - name: Install cached dist + uses: actions/download-artifact@v4 + with: + name: cargo-dist-cache + path: ~/.cargo/bin/ + - name: Setup dist run: | - mkdir -p ~/.cargo/bin - cp $CI_CACHE/dist ~/.cargo/bin/dist chmod +x ~/.cargo/bin/dist echo "$HOME/.cargo/bin" >> $GITHUB_PATH - # Replaced by /ci-cache PVC - uncomment to revert to GitHub artifact storage - # - name: Install cached dist - # uses: actions/download-artifact@v4 - # with: - # name: cargo-dist-cache - # path: ~/.cargo/bin/ - # - name: Setup dist - # run: | - # chmod +x ~/.cargo/bin/dist - # echo "$HOME/.cargo/bin" >> $GITHUB_PATH # Get all the local artifacts for the global tasks to use (for e.g. checksums) - - name: Restore local artifacts from cache - run: | - mkdir -p target/distrib/ - for dir in $CI_CACHE/artifacts-build-local-*; do - [ -d "$dir" ] && cp -r "$dir"/* target/distrib/ - done - # Replaced by /ci-cache PVC - uncomment to revert to GitHub artifact storage - # - name: Fetch local artifacts - # uses: actions/download-artifact@v4 - # with: - # pattern: artifacts-* - # path: target/distrib/ - # merge-multiple: true + - name: Fetch local artifacts + uses: actions/download-artifact@v4 + with: + pattern: artifacts-* + path: target/distrib/ + merge-multiple: true - id: cargo-dist shell: bash run: | @@ -595,32 +370,15 @@ jobs: echo "EOF" >> "$GITHUB_OUTPUT" cp dist-manifest.json "$BUILD_MANIFEST_NAME" - - name: Cache global artifacts - shell: bash - run: | - CACHE_DIR=$CI_CACHE/artifacts-build-global - mkdir -p "$CACHE_DIR" - if test -d "$CACHE_DIR"; then - echo "Cache directory ready: $CACHE_DIR" - else - echo "::error::Cache directory $CACHE_DIR is not available. Is the PVC mounted?" - exit 1 - fi - while IFS= read -r f; do - cp "$f" "$CACHE_DIR/" - done < <(jq --raw-output ".upload_files[]" dist-manifest.json) - cp "$BUILD_MANIFEST_NAME" "$CACHE_DIR/" - echo "Cached global artifacts:" - ls -lh "$CACHE_DIR/" - # Replaced by /ci-cache PVC - uncomment to revert to GitHub artifact storage - # - name: "Upload artifacts" - # uses: actions/upload-artifact@v4 - # with: - # name: artifacts-build-global - # path: | - # ${{ steps.cargo-dist.outputs.paths }} - # ${{ env.BUILD_MANIFEST_NAME }} - # retention-days: 5 + - name: Upload artifacts + uses: actions/upload-artifact@v4 + with: + name: artifacts-build-global + path: | + ${{ steps.cargo-dist.outputs.paths }} + ${{ env.BUILD_MANIFEST_NAME }} + retention-days: 5 + # Build and push Docker images (multi-arch) using cross-compilation # Uses Dockerfile.cross which cross-compiles ARM on x86 (no QEMU emulation) docker: @@ -628,9 +386,7 @@ jobs: needs: [test, lint, frontend, plugins] # Only build Docker on main branch or release tags if: ${{ github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/') }} - # SELF-HOSTED: Change to ubuntu-latest for GitHub runners - # Note: Docker job runs directly on the host (not in a container) to use dind - runs-on: arc-runner-codex + runs-on: ubuntu-latest timeout-minutes: 240 steps: - uses: actions/checkout@v4 @@ -697,67 +453,28 @@ jobs: if: ${{ always() && needs.plan.result == 'success' && needs.plan.outputs.is-release == 'true' && (needs.build-global-artifacts.result == 'skipped' || needs.build-global-artifacts.result == 'success') && (needs.build-local-artifacts.result == 'skipped' || needs.build-local-artifacts.result == 'success') }} env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - # SELF-HOSTED: Change to ubuntu-22.04 for GitHub runners and remove container - runs-on: arc-runner-codex - container: - image: ubuntu:22.04 - volumes: - - /ci-cache:/ci-cache + runs-on: ubuntu-22.04 outputs: val: ${{ steps.host.outputs.manifest }} steps: - - name: Install base dependencies - uses: nick-fields/retry@v3 - with: - timeout_minutes: 10 - max_attempts: 3 - command: | - apt-get update - apt-get install -y git curl jq - # Install GitHub CLI for creating releases - curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg - chmod go+r /usr/share/keyrings/githubcli-archive-keyring.gpg - echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" | tee /etc/apt/sources.list.d/github-cli.list > /dev/null - apt-get update - apt-get install -y gh - - name: Install Rust (required by cargo-dist) - run: | - curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal - echo "$HOME/.cargo/bin" >> $GITHUB_PATH - uses: actions/checkout@v4 with: persist-credentials: false - # Restore dist binary from PVC cache - - name: Restore cached dist + - name: Install cached dist + uses: actions/download-artifact@v4 + with: + name: cargo-dist-cache + path: ~/.cargo/bin/ + - name: Setup dist run: | - mkdir -p ~/.cargo/bin - cp $CI_CACHE/dist ~/.cargo/bin/dist chmod +x ~/.cargo/bin/dist echo "$HOME/.cargo/bin" >> $GITHUB_PATH - # Replaced by /ci-cache PVC - uncomment to revert to GitHub artifact storage - # - name: Install cached dist - # uses: actions/download-artifact@v4 - # with: - # name: cargo-dist-cache - # path: ~/.cargo/bin/ - # - name: Setup dist - # run: | - # chmod +x ~/.cargo/bin/dist - # echo "$HOME/.cargo/bin" >> $GITHUB_PATH - # Fetch artifacts from PVC cache - - name: Restore artifacts from cache - run: | - mkdir -p target/distrib/ - for dir in $CI_CACHE/artifacts-build-*; do - [ -d "$dir" ] && cp -r "$dir"/* target/distrib/ - done - # Replaced by /ci-cache PVC - uncomment to revert to GitHub artifact storage - # - name: Fetch artifacts - # uses: actions/download-artifact@v4 - # with: - # pattern: artifacts-* - # path: target/distrib/ - # merge-multiple: true + - name: Fetch artifacts + uses: actions/download-artifact@v4 + with: + pattern: artifacts-* + path: target/distrib/ + merge-multiple: true - id: host shell: bash run: | @@ -765,34 +482,24 @@ jobs: echo "artifacts uploaded and released successfully" cat dist-manifest.json echo "manifest=$(jq -c "." dist-manifest.json)" >> "$GITHUB_OUTPUT" - # Replaced by /ci-cache PVC - uncomment to revert to GitHub artifact storage - # - name: "Upload dist-manifest.json" - # uses: actions/upload-artifact@v4 - # with: - # # Overwrite the previous copy - # name: artifacts-dist-manifest - # path: dist-manifest.json - # retention-days: 5 - # Create a GitHub Release while uploading all files from cache - - name: Restore release artifacts from cache + - name: Upload dist-manifest.json + uses: actions/upload-artifact@v4 + with: + # Overwrite the previous copy + name: artifacts-dist-manifest + path: dist-manifest.json + retention-days: 5 + # Create a GitHub Release while uploading all files + - name: Download release artifacts + uses: actions/download-artifact@v4 + with: + pattern: artifacts-* + path: artifacts + merge-multiple: true + - name: Cleanup run: | - mkdir -p artifacts/ - for dir in $CI_CACHE/artifacts-build-*; do - [ -d "$dir" ] && cp -r "$dir"/* artifacts/ - done # Remove the granular manifests rm -f artifacts/*-dist-manifest.json - # Replaced by /ci-cache PVC - uncomment to revert to GitHub artifact storage - # - name: "Download GitHub Artifacts" - # uses: actions/download-artifact@v4 - # with: - # pattern: artifacts-* - # path: artifacts - # merge-multiple: true - # - name: Cleanup - # run: | - # # Remove the granular manifests - # rm -f artifacts/*-dist-manifest.json - name: Create GitHub Release env: PRERELEASE_FLAG: "${{ fromJson(steps.host.outputs.manifest).announcement_is_prerelease && '--prerelease' || '' }}" @@ -800,14 +507,6 @@ jobs: ANNOUNCEMENT_BODY: "${{ fromJson(steps.host.outputs.manifest).announcement_github_body }}" RELEASE_COMMIT: "${{ github.sha }}" run: | - # Fix $HOME mismatch in containers / self-hosted runners - export HOME=/github/home - - # Mark directory as safe to avoid "dubious ownership" error in containers - git config --global --add safe.directory "$GITHUB_WORKSPACE" - - # gh uses GH_TOKEN from the environment automatically; no explicit login needed - # Write and read notes from a file to avoid quoting breaking things echo "$ANNOUNCEMENT_BODY" > "$RUNNER_TEMP/notes.txt" @@ -821,18 +520,8 @@ jobs: - plan - host if: ${{ always() && needs.host.result == 'success' }} - # SELF-HOSTED: Change to ubuntu-latest for GitHub runners and remove container - runs-on: arc-runner-codex - container: ubuntu:22.04 + runs-on: ubuntu-latest steps: - - name: Install base dependencies - uses: nick-fields/retry@v3 - with: - timeout_minutes: 10 - max_attempts: 3 - command: | - apt-get update - apt-get install -y git curl - uses: actions/checkout@v4 with: persist-credentials: false @@ -849,10 +538,9 @@ jobs: rm -rf node_modules package-lock.json npm install npm run build - # Note: --provenance is not supported on self-hosted runners - name: Publish to npm working-directory: plugins/sdk-typescript - run: npm publish --access public + run: npm publish --access public --provenance env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} @@ -863,9 +551,7 @@ jobs: - plan - publish-sdk if: ${{ always() && needs.publish-sdk.result == 'success' }} - # SELF-HOSTED: Change to ubuntu-latest for GitHub runners and remove container - runs-on: arc-runner-codex - container: ubuntu:22.04 + runs-on: ubuntu-latest strategy: fail-fast: false matrix: @@ -876,14 +562,6 @@ jobs: - recommendations-anilist - sync-anilist steps: - - name: Install base dependencies - uses: nick-fields/retry@v3 - with: - timeout_minutes: 10 - max_attempts: 3 - command: | - apt-get update - apt-get install -y git curl jq - uses: actions/checkout@v4 with: persist-credentials: false @@ -909,10 +587,9 @@ jobs: - name: Build plugin working-directory: plugins/${{ matrix.plugin }} run: npm run build - # Note: --provenance is not supported on self-hosted runners - name: Publish to npm working-directory: plugins/${{ matrix.plugin }} - run: npm publish --access public + run: npm publish --access public --provenance env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} @@ -926,25 +603,15 @@ jobs: # still allowing individual publish jobs to skip themselves (for prereleases). # "host" however must run to completion, no skipping allowed! if: ${{ always() && needs.host.result == 'success' }} - # SELF-HOSTED: Change to ubuntu-22.04 for GitHub runners and remove container - runs-on: arc-runner-codex - container: ubuntu:22.04 + runs-on: ubuntu-22.04 env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} steps: - - name: Install base dependencies - uses: nick-fields/retry@v3 - with: - timeout_minutes: 10 - max_attempts: 3 - command: | - apt-get update - apt-get install -y git - uses: actions/checkout@v4 with: persist-credentials: false - # Clean up intermediate cache and artifacts that are no longer needed + # Clean up intermediate artifacts that are no longer needed cleanup: name: Cleanup needs: @@ -954,29 +621,14 @@ jobs: - host - docker - announce - # Only clean up when all jobs succeeded — preserve cache on failure so - # individual jobs can be re-run without starting from scratch. - # The 24h orphan cleanup below handles stale caches from old/cancelled runs. if: ${{ !failure() && !cancelled() }} - runs-on: arc-runner-codex - container: - image: ubuntu:22.04 - volumes: - - /ci-cache:/ci-cache + runs-on: ubuntu-latest steps: - - name: Clean up CI cache - run: | - echo "Removing current run cache: $CI_CACHE" - rm -rf $CI_CACHE - # Remove orphaned cache directories older than 24 hours (from cancelled/crashed runs) - echo "Cleaning orphaned cache directories older than 24h..." - find /ci-cache -mindepth 1 -maxdepth 1 -type d -mmin +1440 -exec echo "Removing stale: {}" \; -exec rm -rf {} \; - # Replaced by /ci-cache PVC - uncomment to revert to GitHub artifact storage - # - name: Delete intermediate artifacts - # uses: geekyeggo/delete-artifact@v5 - # with: - # name: | - # nextest-archive - # frontend-dist - # cargo-dist-cache - # artifacts-plan-dist-manifest + - name: Delete intermediate artifacts + uses: geekyeggo/delete-artifact@v5 + with: + name: | + nextest-archive + frontend-dist + cargo-dist-cache + artifacts-plan-dist-manifest diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b630984b..84256063 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,17 +1,5 @@ # CI workflow for Pull Requests # Runs tests, linting, frontend checks, and Docker build with PR tag -# -# ============================================================================= -# SELF-HOSTED RUNNER MODIFICATIONS -# ============================================================================= -# This workflow uses self-hosted runners (arc-runner-codex) with ubuntu:22.04 -# containers. To revert to paid GitHub runners: -# -# 1. Replace all `runs-on: arc-runner-codex` with `runs-on: ubuntu-latest` -# 2. Remove all `container: ubuntu:22.04` lines -# 3. Remove all "Install base dependencies" steps -# 4. Remove all "Install Rust" steps (GitHub runners have Rust pre-installed) -# ============================================================================= name: CI @@ -29,122 +17,67 @@ concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true -# Shared cache directory on PVC mounted by ARC runners -# Each workflow run gets its own isolated directory under /ci-cache -env: - CI_CACHE: /ci-cache/${{ github.run_id }} - jobs: # Build test binaries and create nextest archive build-tests: name: Build Tests - # SELF-HOSTED: Change to ubuntu-latest for GitHub runners and remove container - runs-on: arc-runner-codex - container: - image: ubuntu:22.04 - volumes: - - /ci-cache:/ci-cache + runs-on: ubuntu-latest timeout-minutes: 60 env: SCCACHE_GHA_ENABLED: "true" SCCACHE_GHA_VERSION: test RUSTC_WRAPPER: sccache steps: - - name: Install base dependencies - uses: nick-fields/retry@v3 - with: - timeout_minutes: 10 - max_attempts: 3 - command: | - apt-get update - apt-get install -y git curl build-essential pkg-config clang mold - uses: actions/checkout@v4 - - name: Install Rust - uses: dtolnay/rust-toolchain@stable + - name: Install mold linker + run: sudo apt-get update && sudo apt-get install -y mold - name: Install cargo-nextest uses: taiki-e/install-action@nextest - name: Setup sccache uses: mozilla-actions/sccache-action@v0.0.9 - - name: Prepare cache directory - run: | - mkdir -p $CI_CACHE - if test -d $CI_CACHE; then - echo "CI_CACHE directory ready: $CI_CACHE" - ls -la $(dirname $CI_CACHE) - else - echo "::error::CI_CACHE directory $CI_CACHE is not available. Is the PVC mounted?" - exit 1 - fi - name: Build and archive tests - run: | - cargo nextest archive --features rar --archive-file $CI_CACHE/nextest-archive.tar.zst - ls -lh $CI_CACHE/nextest-archive.tar.zst || { echo "::error::Failed to write nextest archive to cache"; exit 1; } - # Replaced by /ci-cache PVC - uncomment to revert to GitHub artifact storage - # - name: Upload test archive - # uses: actions/upload-artifact@v4 - # with: - # name: nextest-archive - # path: nextest-archive.tar.zst - # retention-days: 1 + run: cargo nextest archive --features rar --archive-file nextest-archive.tar.zst + - name: Upload test archive + uses: actions/upload-artifact@v4 + with: + name: nextest-archive + path: nextest-archive.tar.zst + retention-days: 1 # Run tests in parallel partitions test: name: Test (${{ matrix.partition }}/5) needs: build-tests - # SELF-HOSTED: Change to ubuntu-latest for GitHub runners and remove container - runs-on: arc-runner-codex - container: - image: ubuntu:22.04 - volumes: - - /ci-cache:/ci-cache + runs-on: ubuntu-latest timeout-minutes: 60 strategy: fail-fast: false matrix: partition: [1, 2, 3, 4, 5] steps: - - name: Install base dependencies - uses: nick-fields/retry@v3 - with: - timeout_minutes: 10 - max_attempts: 3 - command: | - apt-get update - apt-get install -y git curl - uses: actions/checkout@v4 - - name: Install Rust - uses: dtolnay/rust-toolchain@stable - name: Install cargo-nextest uses: taiki-e/install-action@nextest - # Replaced by /ci-cache PVC - uncomment to revert to GitHub artifact storage - # - name: Download test archive - # uses: actions/download-artifact@v4 - # with: - # name: nextest-archive + - name: Download test archive + uses: actions/download-artifact@v4 + with: + name: nextest-archive - name: Run tests (partition ${{ matrix.partition }}/5) - run: cargo nextest run --archive-file $CI_CACHE/nextest-archive.tar.zst --partition hash:${{ matrix.partition }}/5 + run: cargo nextest run --archive-file nextest-archive.tar.zst --partition hash:${{ matrix.partition }}/5 # Run linting checks lint: name: Lint - # SELF-HOSTED: Change to ubuntu-latest for GitHub runners and remove container - runs-on: arc-runner-codex - container: ubuntu:22.04 + runs-on: ubuntu-latest timeout-minutes: 30 env: SCCACHE_GHA_ENABLED: "true" SCCACHE_GHA_VERSION: lint RUSTC_WRAPPER: sccache steps: - - name: Install base dependencies - uses: nick-fields/retry@v3 - with: - timeout_minutes: 10 - max_attempts: 3 - command: | - apt-get update - apt-get install -y git curl build-essential pkg-config clang mold - uses: actions/checkout@v4 + - name: Install mold linker + run: sudo apt-get update && sudo apt-get install -y mold - name: Install Rust uses: dtolnay/rust-toolchain@stable with: @@ -159,19 +92,9 @@ jobs: # Run frontend tests and build frontend: name: Frontend - # SELF-HOSTED: Change to ubuntu-latest for GitHub runners and remove container - runs-on: arc-runner-codex - container: ubuntu:22.04 + runs-on: ubuntu-latest timeout-minutes: 30 steps: - - name: Install base dependencies - uses: nick-fields/retry@v3 - with: - timeout_minutes: 10 - max_attempts: 3 - command: | - apt-get update - apt-get install -y git curl - uses: actions/checkout@v4 - name: Setup Node.js uses: actions/setup-node@v4 @@ -198,9 +121,7 @@ jobs: # Run plugin checks (lint, typecheck, tests) plugins: name: Plugins (${{ matrix.plugin }}) - # SELF-HOSTED: Change to ubuntu-latest for GitHub runners and remove container - runs-on: arc-runner-codex - container: ubuntu:22.04 + runs-on: ubuntu-latest timeout-minutes: 30 strategy: fail-fast: false @@ -213,14 +134,6 @@ jobs: - recommendations-anilist - sync-anilist steps: - - name: Install base dependencies - uses: nick-fields/retry@v3 - with: - timeout_minutes: 10 - max_attempts: 3 - command: | - apt-get update - apt-get install -y git curl - uses: actions/checkout@v4 - name: Setup Node.js uses: actions/setup-node@v4 @@ -256,7 +169,7 @@ jobs: docker: name: Docker needs: [test, lint, frontend, plugins] - runs-on: arc-runner-codex + runs-on: ubuntu-latest timeout-minutes: 240 steps: - uses: actions/checkout@v4 @@ -292,31 +205,16 @@ jobs: cache-from: type=registry,ref=ghcr.io/${{ env.DOCKER_REGISTRY }}-buildcache:latest cache-to: type=registry,ref=ghcr.io/${{ env.DOCKER_REGISTRY }}-buildcache:latest,mode=max - # Clean up intermediate cache and artifacts that are no longer needed + # Clean up intermediate artifacts cleanup: name: Cleanup needs: - test - docker - # Only clean up when all jobs succeeded — preserve cache on failure so - # individual jobs can be re-run without starting from scratch. - # The 24h orphan cleanup below handles stale caches from old/cancelled runs. if: ${{ !failure() && !cancelled() }} - runs-on: arc-runner-codex - container: - image: ubuntu:22.04 - volumes: - - /ci-cache:/ci-cache + runs-on: ubuntu-latest steps: - - name: Clean up CI cache - run: | - echo "Removing current run cache: $CI_CACHE" - rm -rf $CI_CACHE - # Remove orphaned cache directories older than 24 hours (from cancelled/crashed runs) - echo "Cleaning orphaned cache directories older than 24h..." - find /ci-cache -mindepth 1 -maxdepth 1 -type d -mmin +1440 -exec echo "Removing stale: {}" \; -exec rm -rf {} \; - # Replaced by /ci-cache PVC - uncomment to revert to GitHub artifact storage - # - name: Delete intermediate artifacts - # uses: geekyeggo/delete-artifact@v5 - # with: - # name: nextest-archive + - name: Delete intermediate artifacts + uses: geekyeggo/delete-artifact@v5 + with: + name: nextest-archive diff --git a/Cargo.toml b/Cargo.toml index 7bc94996..79a984e2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -191,18 +191,7 @@ ci = "github" # The installers to generate for each app installers = ["shell", "powershell"] # Target platforms to build apps for (Rust target-triple syntax) -# ============================================================================= -# SELF-HOSTED RUNNER MODIFICATIONS -# ============================================================================= -# To revert to paid GitHub runners: -# 1. Uncomment the full targets list below -# 2. Comment out or remove the Linux-only targets line -# 3. Remove both [workspace.metadata.dist.github-custom-runners.*] sections below -# ============================================================================= -# ORIGINAL (GitHub runners): Uncomment this line for paid runners -# targets = ["aarch64-apple-darwin", "aarch64-unknown-linux-gnu", "x86_64-apple-darwin", "x86_64-unknown-linux-gnu", "x86_64-pc-windows-msvc"] -# SELF-HOSTED: Only Linux targets - macOS/Windows require GitHub-hosted runners (paid) -targets = ["x86_64-unknown-linux-gnu"] +targets = ["aarch64-apple-darwin", "aarch64-unknown-linux-gnu", "x86_64-apple-darwin", "x86_64-unknown-linux-gnu", "x86_64-pc-windows-msvc"] # Which actions to run on pull requests pr-run-mode = "plan" # Whether to install an updater program @@ -213,14 +202,3 @@ install-path = "CARGO_HOME" github-attestations = true # Allow customized workflow file (we use build.yml instead of release.yml) allow-dirty = ["ci"] - -# ============================================================================= -# SELF-HOSTED: Custom runner configurations -# Remove this section when reverting to paid GitHub runners -# ============================================================================= - -# SELF-HOSTED: Use self-hosted runner for x86_64 Linux builds -[workspace.metadata.dist.github-custom-runners.x86_64-unknown-linux-gnu] -runner = "arc-runner-codex" -features = ["embed-frontend"] -default-features = false diff --git a/README.md b/README.md index f1a7b463..334bf8d1 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,8 @@ A next-generation digital library server for comics, manga, and ebooks built in Rust. Designed to scale horizontally while remaining simple for homelab deployments. +> **Note:** Codex is under active development. Architecture and APIs may still change as the project matures. There is a sizable backlog of planned work, so responses to feature requests may be slow. Bug reports are always welcome. + ## Features - **Dual database support**: SQLite for simple setups, PostgreSQL for production @@ -24,7 +26,7 @@ Start the server: codex serve --config codex.yaml ``` -See [codex-sample.yaml](codex-sample.yaml) for configuration options. +See [config/config.sqlite.yaml](config/config.sqlite.yaml) for configuration options. ## Development Setup @@ -73,31 +75,14 @@ cargo test --features rar cbr_parser For more details, see the [UnRAR license](https://www.rarlab.com/license.htm). -## Recent Updates - -### Graceful Shutdown Fix (January 2026) - -Fixed critical performance issue where page reloads and container restarts would hang for 40-70 seconds: - -- ✅ Backend worker tasks now shutdown gracefully (2-5s instead of 10+s) -- ✅ SSE connections properly detect disconnects (< 1s instead of 30-60s) -- ✅ Page reloads are now instant (1-2s instead of 40-70s) - -See [Troubleshooting Guide](docs/docs/troubleshooting.md) for details. - ## Documentation -- [Getting Started](docs/docs/getting-started.md) -- [Configuration](docs/docs/configuration.md) -- [API Documentation](docs/docs/api.md) -- [Troubleshooting](docs/docs/troubleshooting.md) -- [Technical Details](GRACEFUL_SHUTDOWN.md) +- [Getting Started](https://codex.4sh.dev/docs/getting-started) +- [Configuration](https://codex.4sh.dev/docs/configuration) +- [API Documentation](https://codex.4sh.dev/docs/api/codex-api) +- [Troubleshooting](https://codex.4sh.dev/docs/troubleshooting) - [Changelog](CHANGELOG.md) -## Project Status - -Currently in Phase 1 (MVP Core). See [implementation docs](tmp/impl/overview.md) for the roadmap. - ## License This project is licensed under the [GNU Affero General Public License v3.0](LICENSE). diff --git a/docs/api/openapi.json b/docs/api/openapi.json index fc52ca57..ed748d98 100644 --- a/docs/api/openapi.json +++ b/docs/api/openapi.json @@ -2,7 +2,7 @@ "openapi": "3.1.0", "info": { "title": "Codex API", - "description": "REST API for Codex, a digital library server for comics, manga, and ebooks.\n\n## Interactive API Documentation\n\nYou can explore and test this API interactively:\n\n- **Hosted Documentation**: Visit [codex.4sh.dev/docs/api](https://codex.4sh.dev/docs/api) for the full API reference\n- **Your Instance**: If you have Scalar UI enabled, access `/api/docs` on your Codex server\n\n## Authentication\n\nMost endpoints require authentication. Codex supports two authentication methods:\n\n1. **JWT Bearer Token**: Obtain a token via `POST /api/v1/auth/login`, then include it as `Authorization: Bearer `\n2. **API Key**: Generate an API key in the web UI or via the API, then include it as `X-API-Key: ` header\n\n## OPDS Support\n\nCodex provides OPDS catalog feeds for e-reader applications:\n\n- **OPDS 1.2** (Atom XML): `/opds/v1/catalog` - Compatible with most e-readers\n- **OPDS 2.0** (JSON): `/opds/v2` - Modern JSON-based format with enhanced features\n\n## Komga-Compatible API\n\nCodex provides an optional Komga-compatible API for third-party apps like Komic:\n\n- **Disabled by default** - Enable via `komga_api.enabled: true` in config\n- **Configurable prefix** - Default path: `/{prefix}/api/v1/` where prefix defaults to `komga`\n- **Same authentication** - Supports JWT, API keys, and Basic Auth\n\nNote: The `{prefix}` path parameter in Komga endpoints is configurable at runtime.\n\n## Pagination\n\nList endpoints support pagination with the following conventions:\n\n### Query Parameters\n\nAll endpoints (GET and POST) use query parameters for pagination:\n\n| Parameter | Default | Max | Description |\n|-----------|---------|-----|-------------|\n| `page` | `1` | - | Page number (1-indexed) |\n| `pageSize` | `50` | `500` | Items per page |\n\nExample GET: `GET /api/v1/books?page=2&pageSize=25`\n\nExample POST: `POST /api/v1/series/list?page=1&pageSize=25&sort=name,asc`\n\nFor POST endpoints like `/api/v1/books/list` and `/api/v1/series/list`:\n- Pagination parameters (`page`, `pageSize`, `sort`) go in the **query string**\n- Filter criteria (`condition`, `fullTextSearch`) go in the **request body**\n\n### Response Format\n\nAll paginated responses use camelCase and include HATEOAS navigation links:\n\n```json\n{\n \"data\": [...],\n \"page\": 1,\n \"pageSize\": 25,\n \"total\": 150,\n \"totalPages\": 6,\n \"links\": {\n \"self\": \"/api/v1/series/list?page=1&pageSize=25\",\n \"first\": \"/api/v1/series/list?page=1&pageSize=25\",\n \"next\": \"/api/v1/series/list?page=2&pageSize=25\",\n \"last\": \"/api/v1/series/list?page=6&pageSize=25\"\n }\n}\n```\n\n## Rate Limiting\n\nAll API endpoints are protected by rate limiting (enabled by default). Rate limits use a token bucket algorithm with separate limits for anonymous and authenticated users.\n\n### Limits\n\n| Client Type | Requests/Second | Burst Size |\n|-------------|-----------------|------------|\n| Anonymous (by IP) | 10 | 50 |\n| Authenticated (by user) | 50 | 200 |\n\n### Response Headers\n\nAll responses include rate limit information:\n\n| Header | Description |\n|--------|-------------|\n| `X-RateLimit-Limit` | Maximum requests allowed (burst size) |\n| `X-RateLimit-Remaining` | Requests remaining |\n| `X-RateLimit-Reset` | Seconds until a token is available |\n\n### 429 Too Many Requests\n\nWhen rate limited, the API returns HTTP 429 with a `Retry-After` header:\n\n```json\n{\n \"error\": \"rate_limit_exceeded\",\n \"message\": \"Too many requests. Please retry after 30 seconds.\",\n \"retry_after\": 30\n}\n```\n\n### Exempt Paths\n\nThe following paths are exempt from rate limiting:\n- `/health` - Health check endpoint\n- `/api/v1/events` - SSE event stream", + "description": "REST API for Codex, a digital library server for comics, manga, and ebooks.\n\n## Interactive API Documentation\n\nYou can explore and test this API interactively:\n\n- **Hosted Documentation**: Visit [codex.4sh.dev/docs/api/codex-api](https://codex.4sh.dev/docs/api/codex-api) for the full API reference\n- **Your Instance**: If you have Scalar UI enabled, access `/api/docs` on your Codex server\n\n## Authentication\n\nMost endpoints require authentication. Codex supports two authentication methods:\n\n1. **JWT Bearer Token**: Obtain a token via `POST /api/v1/auth/login`, then include it as `Authorization: Bearer `\n2. **API Key**: Generate an API key in the web UI or via the API, then include it as `X-API-Key: ` header\n\n## OPDS Support\n\nCodex provides OPDS catalog feeds for e-reader applications:\n\n- **OPDS 1.2** (Atom XML): `/opds/v1/catalog` - Compatible with most e-readers\n- **OPDS 2.0** (JSON): `/opds/v2` - Modern JSON-based format with enhanced features\n\n## Komga-Compatible API\n\nCodex provides an optional Komga-compatible API for third-party apps like Komic:\n\n- **Disabled by default** - Enable via `komga_api.enabled: true` in config\n- **Configurable prefix** - Default path: `/{prefix}/api/v1/` where prefix defaults to `komga`\n- **Same authentication** - Supports JWT, API keys, and Basic Auth\n\nNote: The `{prefix}` path parameter in Komga endpoints is configurable at runtime.\n\n## Pagination\n\nList endpoints support pagination with the following conventions:\n\n### Query Parameters\n\nAll endpoints (GET and POST) use query parameters for pagination:\n\n| Parameter | Default | Max | Description |\n|-----------|---------|-----|-------------|\n| `page` | `1` | - | Page number (1-indexed) |\n| `pageSize` | `50` | `500` | Items per page |\n\nExample GET: `GET /api/v1/books?page=2&pageSize=25`\n\nExample POST: `POST /api/v1/series/list?page=1&pageSize=25&sort=name,asc`\n\nFor POST endpoints like `/api/v1/books/list` and `/api/v1/series/list`:\n- Pagination parameters (`page`, `pageSize`, `sort`) go in the **query string**\n- Filter criteria (`condition`, `fullTextSearch`) go in the **request body**\n\n### Response Format\n\nAll paginated responses use camelCase and include HATEOAS navigation links:\n\n```json\n{\n \"data\": [...],\n \"page\": 1,\n \"pageSize\": 25,\n \"total\": 150,\n \"totalPages\": 6,\n \"links\": {\n \"self\": \"/api/v1/series/list?page=1&pageSize=25\",\n \"first\": \"/api/v1/series/list?page=1&pageSize=25\",\n \"next\": \"/api/v1/series/list?page=2&pageSize=25\",\n \"last\": \"/api/v1/series/list?page=6&pageSize=25\"\n }\n}\n```\n\n## Rate Limiting\n\nAll API endpoints are protected by rate limiting (enabled by default). Rate limits use a token bucket algorithm with separate limits for anonymous and authenticated users.\n\n### Limits\n\n| Client Type | Requests/Second | Burst Size |\n|-------------|-----------------|------------|\n| Anonymous (by IP) | 10 | 50 |\n| Authenticated (by user) | 50 | 200 |\n\n### Response Headers\n\nAll responses include rate limit information:\n\n| Header | Description |\n|--------|-------------|\n| `X-RateLimit-Limit` | Maximum requests allowed (burst size) |\n| `X-RateLimit-Remaining` | Requests remaining |\n| `X-RateLimit-Reset` | Seconds until a token is available |\n\n### 429 Too Many Requests\n\nWhen rate limited, the API returns HTTP 429 with a `Retry-After` header:\n\n```json\n{\n \"error\": \"rate_limit_exceeded\",\n \"message\": \"Too many requests. Please retry after 30 seconds.\",\n \"retry_after\": 30\n}\n```\n\n### Exempt Paths\n\nThe following paths are exempt from rate limiting:\n- `/health` - Health check endpoint\n- `/api/v1/events` - SSE event stream", "license": { "name": "MIT", "url": "https://opensource.org/licenses/MIT" diff --git a/docs/dev/intro.md b/docs/dev/intro.md index 1777097a..75283bf3 100644 --- a/docs/dev/intro.md +++ b/docs/dev/intro.md @@ -27,6 +27,6 @@ Help improve Codex itself: ## Quick Links -- [Feature Board](https://codex.userjot.com) +- [Issues](https://github.com/AshDevFr/codex/issues) - [Documentation](https://codex.4sh.dev) - [API Reference](/docs/api/codex-api) diff --git a/docs/docs/intro.md b/docs/docs/intro.md index 84524e86..39f7ccd7 100644 --- a/docs/docs/intro.md +++ b/docs/docs/intro.md @@ -140,7 +140,7 @@ Codex is actively developed and used in production environments. While the core ## Getting Help - **Documentation**: You're reading it! -- **Feature Board**: [Report bugs or request features](https://codex.userjot.com) +- **Issues**: [Report bugs or request features](https://github.com/AshDevFr/codex/issues) ## License diff --git a/docs/docs/troubleshooting.md b/docs/docs/troubleshooting.md index 53a0df08..2b401d1f 100644 --- a/docs/docs/troubleshooting.md +++ b/docs/docs/troubleshooting.md @@ -802,7 +802,7 @@ Go to **Settings** > **Metrics** to view server statistics including inventory c ### Report Issues -- [Feature Board](https://codex.userjot.com) +- [Issues](https://github.com/AshDevFr/codex/issues) - Include: - Steps to reproduce - Expected vs actual behavior diff --git a/docs/docusaurus.config.ts b/docs/docusaurus.config.ts index 335529b1..8108e43d 100644 --- a/docs/docusaurus.config.ts +++ b/docs/docusaurus.config.ts @@ -150,8 +150,8 @@ const config: Config = { label: 'API', }, { - href: 'https://codex.userjot.com', - label: 'Feature Board', + href: 'https://github.com/AshDevFr/codex', + label: 'GitHub', position: 'right', }, ], @@ -172,7 +172,7 @@ const config: Config = { }, { label: 'API Reference', - to: '/docs/api', + to: '/docs/api/codex-api', }, ], }, @@ -193,8 +193,12 @@ const config: Config = { title: 'Project', items: [ { - label: 'Feature Board', - href: 'https://codex.userjot.com', + label: 'GitHub', + href: 'https://github.com/AshDevFr/codex', + }, + { + label: 'Issues', + href: 'https://github.com/AshDevFr/codex/issues', }, { label: 'Documentation', diff --git a/docs/src/pages/index.tsx b/docs/src/pages/index.tsx index 6c450b0a..9c4e5c59 100644 --- a/docs/src/pages/index.tsx +++ b/docs/src/pages/index.tsx @@ -51,7 +51,7 @@ function QuickLinks(): ReactNode {
- + 🔌 API Reference diff --git a/src/api/docs.rs b/src/api/docs.rs index c03d09d7..e8646e61 100644 --- a/src/api/docs.rs +++ b/src/api/docs.rs @@ -19,7 +19,7 @@ use utoipa::OpenApi; You can explore and test this API interactively: -- **Hosted Documentation**: Visit [codex.4sh.dev/docs/api](https://codex.4sh.dev/docs/api) for the full API reference +- **Hosted Documentation**: Visit [codex.4sh.dev/docs/api/codex-api](https://codex.4sh.dev/docs/api/codex-api) for the full API reference - **Your Instance**: If you have Scalar UI enabled, access `/api/docs` on your Codex server ## Authentication diff --git a/web/openapi.json b/web/openapi.json index fc52ca57..ed748d98 100644 --- a/web/openapi.json +++ b/web/openapi.json @@ -2,7 +2,7 @@ "openapi": "3.1.0", "info": { "title": "Codex API", - "description": "REST API for Codex, a digital library server for comics, manga, and ebooks.\n\n## Interactive API Documentation\n\nYou can explore and test this API interactively:\n\n- **Hosted Documentation**: Visit [codex.4sh.dev/docs/api](https://codex.4sh.dev/docs/api) for the full API reference\n- **Your Instance**: If you have Scalar UI enabled, access `/api/docs` on your Codex server\n\n## Authentication\n\nMost endpoints require authentication. Codex supports two authentication methods:\n\n1. **JWT Bearer Token**: Obtain a token via `POST /api/v1/auth/login`, then include it as `Authorization: Bearer `\n2. **API Key**: Generate an API key in the web UI or via the API, then include it as `X-API-Key: ` header\n\n## OPDS Support\n\nCodex provides OPDS catalog feeds for e-reader applications:\n\n- **OPDS 1.2** (Atom XML): `/opds/v1/catalog` - Compatible with most e-readers\n- **OPDS 2.0** (JSON): `/opds/v2` - Modern JSON-based format with enhanced features\n\n## Komga-Compatible API\n\nCodex provides an optional Komga-compatible API for third-party apps like Komic:\n\n- **Disabled by default** - Enable via `komga_api.enabled: true` in config\n- **Configurable prefix** - Default path: `/{prefix}/api/v1/` where prefix defaults to `komga`\n- **Same authentication** - Supports JWT, API keys, and Basic Auth\n\nNote: The `{prefix}` path parameter in Komga endpoints is configurable at runtime.\n\n## Pagination\n\nList endpoints support pagination with the following conventions:\n\n### Query Parameters\n\nAll endpoints (GET and POST) use query parameters for pagination:\n\n| Parameter | Default | Max | Description |\n|-----------|---------|-----|-------------|\n| `page` | `1` | - | Page number (1-indexed) |\n| `pageSize` | `50` | `500` | Items per page |\n\nExample GET: `GET /api/v1/books?page=2&pageSize=25`\n\nExample POST: `POST /api/v1/series/list?page=1&pageSize=25&sort=name,asc`\n\nFor POST endpoints like `/api/v1/books/list` and `/api/v1/series/list`:\n- Pagination parameters (`page`, `pageSize`, `sort`) go in the **query string**\n- Filter criteria (`condition`, `fullTextSearch`) go in the **request body**\n\n### Response Format\n\nAll paginated responses use camelCase and include HATEOAS navigation links:\n\n```json\n{\n \"data\": [...],\n \"page\": 1,\n \"pageSize\": 25,\n \"total\": 150,\n \"totalPages\": 6,\n \"links\": {\n \"self\": \"/api/v1/series/list?page=1&pageSize=25\",\n \"first\": \"/api/v1/series/list?page=1&pageSize=25\",\n \"next\": \"/api/v1/series/list?page=2&pageSize=25\",\n \"last\": \"/api/v1/series/list?page=6&pageSize=25\"\n }\n}\n```\n\n## Rate Limiting\n\nAll API endpoints are protected by rate limiting (enabled by default). Rate limits use a token bucket algorithm with separate limits for anonymous and authenticated users.\n\n### Limits\n\n| Client Type | Requests/Second | Burst Size |\n|-------------|-----------------|------------|\n| Anonymous (by IP) | 10 | 50 |\n| Authenticated (by user) | 50 | 200 |\n\n### Response Headers\n\nAll responses include rate limit information:\n\n| Header | Description |\n|--------|-------------|\n| `X-RateLimit-Limit` | Maximum requests allowed (burst size) |\n| `X-RateLimit-Remaining` | Requests remaining |\n| `X-RateLimit-Reset` | Seconds until a token is available |\n\n### 429 Too Many Requests\n\nWhen rate limited, the API returns HTTP 429 with a `Retry-After` header:\n\n```json\n{\n \"error\": \"rate_limit_exceeded\",\n \"message\": \"Too many requests. Please retry after 30 seconds.\",\n \"retry_after\": 30\n}\n```\n\n### Exempt Paths\n\nThe following paths are exempt from rate limiting:\n- `/health` - Health check endpoint\n- `/api/v1/events` - SSE event stream", + "description": "REST API for Codex, a digital library server for comics, manga, and ebooks.\n\n## Interactive API Documentation\n\nYou can explore and test this API interactively:\n\n- **Hosted Documentation**: Visit [codex.4sh.dev/docs/api/codex-api](https://codex.4sh.dev/docs/api/codex-api) for the full API reference\n- **Your Instance**: If you have Scalar UI enabled, access `/api/docs` on your Codex server\n\n## Authentication\n\nMost endpoints require authentication. Codex supports two authentication methods:\n\n1. **JWT Bearer Token**: Obtain a token via `POST /api/v1/auth/login`, then include it as `Authorization: Bearer `\n2. **API Key**: Generate an API key in the web UI or via the API, then include it as `X-API-Key: ` header\n\n## OPDS Support\n\nCodex provides OPDS catalog feeds for e-reader applications:\n\n- **OPDS 1.2** (Atom XML): `/opds/v1/catalog` - Compatible with most e-readers\n- **OPDS 2.0** (JSON): `/opds/v2` - Modern JSON-based format with enhanced features\n\n## Komga-Compatible API\n\nCodex provides an optional Komga-compatible API for third-party apps like Komic:\n\n- **Disabled by default** - Enable via `komga_api.enabled: true` in config\n- **Configurable prefix** - Default path: `/{prefix}/api/v1/` where prefix defaults to `komga`\n- **Same authentication** - Supports JWT, API keys, and Basic Auth\n\nNote: The `{prefix}` path parameter in Komga endpoints is configurable at runtime.\n\n## Pagination\n\nList endpoints support pagination with the following conventions:\n\n### Query Parameters\n\nAll endpoints (GET and POST) use query parameters for pagination:\n\n| Parameter | Default | Max | Description |\n|-----------|---------|-----|-------------|\n| `page` | `1` | - | Page number (1-indexed) |\n| `pageSize` | `50` | `500` | Items per page |\n\nExample GET: `GET /api/v1/books?page=2&pageSize=25`\n\nExample POST: `POST /api/v1/series/list?page=1&pageSize=25&sort=name,asc`\n\nFor POST endpoints like `/api/v1/books/list` and `/api/v1/series/list`:\n- Pagination parameters (`page`, `pageSize`, `sort`) go in the **query string**\n- Filter criteria (`condition`, `fullTextSearch`) go in the **request body**\n\n### Response Format\n\nAll paginated responses use camelCase and include HATEOAS navigation links:\n\n```json\n{\n \"data\": [...],\n \"page\": 1,\n \"pageSize\": 25,\n \"total\": 150,\n \"totalPages\": 6,\n \"links\": {\n \"self\": \"/api/v1/series/list?page=1&pageSize=25\",\n \"first\": \"/api/v1/series/list?page=1&pageSize=25\",\n \"next\": \"/api/v1/series/list?page=2&pageSize=25\",\n \"last\": \"/api/v1/series/list?page=6&pageSize=25\"\n }\n}\n```\n\n## Rate Limiting\n\nAll API endpoints are protected by rate limiting (enabled by default). Rate limits use a token bucket algorithm with separate limits for anonymous and authenticated users.\n\n### Limits\n\n| Client Type | Requests/Second | Burst Size |\n|-------------|-----------------|------------|\n| Anonymous (by IP) | 10 | 50 |\n| Authenticated (by user) | 50 | 200 |\n\n### Response Headers\n\nAll responses include rate limit information:\n\n| Header | Description |\n|--------|-------------|\n| `X-RateLimit-Limit` | Maximum requests allowed (burst size) |\n| `X-RateLimit-Remaining` | Requests remaining |\n| `X-RateLimit-Reset` | Seconds until a token is available |\n\n### 429 Too Many Requests\n\nWhen rate limited, the API returns HTTP 429 with a `Retry-After` header:\n\n```json\n{\n \"error\": \"rate_limit_exceeded\",\n \"message\": \"Too many requests. Please retry after 30 seconds.\",\n \"retry_after\": 30\n}\n```\n\n### Exempt Paths\n\nThe following paths are exempt from rate limiting:\n- `/health` - Health check endpoint\n- `/api/v1/events` - SSE event stream", "license": { "name": "MIT", "url": "https://opensource.org/licenses/MIT"