diff --git a/.dockerignore b/.dockerignore deleted file mode 100644 index 2311a50..0000000 --- a/.dockerignore +++ /dev/null @@ -1,3 +0,0 @@ -.git/ -.cache/ -.github/workflows diff --git a/.github/dependabot.yml b/.github/dependabot.yml index f278062..afaf0bb 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -8,9 +8,3 @@ updates: directory: "/" schedule: interval: daily - - package-ecosystem: docker - directory: "/" - schedule: - interval: daily - labels: - - automerge diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a6cc889..5043637 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,34 +14,60 @@ concurrency: env: ALLOWERRORS: 'false' NBCACHE: '.cache' + CACHE_NUM: '1' + JULIA_CONDAPKG_BACKEND: 'Null' + JULIA_NUM_THREADS: 'auto' + JULIA_CI: 'true' + UV_SYSTEM_PYTHON: 1 jobs: setup: - permissions: - packages: write runs-on: ubuntu-latest outputs: matrix: ${{ steps.set-matrix.outputs.matrix }} - hash: ${{ steps.img.outputs.hash }} + hash: ${{ steps.hash.outputs.value }} steps: - name: Checkout repository uses: actions/checkout@v4 - - name: Login to ghcr.io - uses: docker/login-action@v3 + - name: Setup Python + uses: actions/setup-python@v5 + id: setup-python + with: + python-version: '3.x' + - name: Read Julia version + uses: SebRollen/toml-action@v1.2.0 + id: read_toml + with: + file: 'Manifest.toml' + field: 'julia_version' + - name: Setup Julia + uses: julia-actions/setup-julia@v2 with: - registry: ghcr.io - username: ${{ github.repository_owner }} - password: ${{ github.token }} - - name: Get docker image hash - id: img - run: echo "hash=${{ hashFiles('requirements.txt', 'Project.toml', 'Manifest.toml', 'src/**', 'env.Dockerfile') }}" >> "$GITHUB_OUTPUT" - - name: Build Docker container - env: - IMG: ghcr.io/${{ github.repository }}:${{ steps.img.outputs.hash }} + version: ${{ steps.read_toml.outputs.value }} + - name: Get environment hash + id: hash + run: echo "value=${{ hashFiles('Project.toml', 'Manifest.toml', 'src/**') }}" >> "$GITHUB_OUTPUT" + - name: Cache Julia packages + if: ${{ contains(runner.name, 'GitHub Actions') }} + uses: actions/cache@v4 + id: cache-julia + with: + path: | + ~/.julia + !~/.julia/registries + key: ${{ runner.os }}-julia-${{ env.CACHE_NUM }}-${{ steps.read_toml.outputs.value }}-${{ steps.hash.outputs.value }} + restore-keys: | + ${{ runner.os }}-julia-${{ env.CACHE_NUM }}-${{ steps.read_toml.outputs.value }}- + - name: Install Julia packages + if: ${{ !contains(runner.name, 'GitHub Actions') || steps.cache-julia.outputs.cache-hit != 'true' }} + shell: julia --color=yes {0} run: | - docker manifest inspect ${IMG} && exit 0 - docker build -f env.Dockerfile -t ${IMG} . - docker push ${IMG} + using Pkg, Dates + Pkg.add(["IJulia", "Literate", "PrettyTables", "JSON"]) + Pkg.activate(".") + Pkg.instantiate() + Pkg.precompile() + Pkg.gc(collect_delay=Day(0)) - name: List notebooks as a JSON array id: set-matrix run: echo "matrix=$(python -c 'import glob, json; print(json.dumps(glob.glob("**/*.ipynb", root_dir="docs", recursive=True) + glob.glob("**/*.jl", root_dir="docs",recursive=True)))')" >> "$GITHUB_OUTPUT" @@ -57,7 +83,6 @@ jobs: runs-on: ubuntu-latest env: NB: docs/${{ matrix.notebook }} - IMG: ghcr.io/${{ github.repository }}:${{ needs.setup.outputs.hash }} steps: - name: Checkout uses: actions/checkout@v4 @@ -67,25 +92,42 @@ jobs: with: path: ${{ env.NBCACHE }} key: ${{ runner.os }}-notebook-${{ hashFiles(env.NB) }}-${{ needs.setup.outputs.hash }} - - name: Pull docker image + - name: Setup Python + uses: actions/setup-python@v5 + if: ${{ steps.nb-cache.outputs.cache-hit != 'true' }} + with: + python-version: '3.x' + - name: Install uv if: ${{ steps.nb-cache.outputs.cache-hit != 'true' }} - run: | - docker pull ${{ env.IMG }} - docker images ${{ env.IMG }} + run: curl -LsSf https://astral.sh/uv/install.sh | sh + - name: Install Python dependencies + if: ${{ steps.nb-cache.outputs.cache-hit != 'true' }} + run: uv pip install -r requirements.txt + - name: Read Julia version + if: ${{ steps.nb-cache.outputs.cache-hit != 'true' }} + uses: SebRollen/toml-action@v1.2.0 + id: read_toml + with: + file: 'Manifest.toml' + field: 'julia_version' + - name: Setup Julia + if: ${{ steps.nb-cache.outputs.cache-hit != 'true' }} + uses: julia-actions/setup-julia@v2 + with: + version: ${{ steps.read_toml.outputs.value }} + - name: Restore Julia packages + if: ${{ steps.nb-cache.outputs.cache-hit != 'true' && contains(runner.name, 'GitHub Actions')}} + uses: actions/cache/restore@v4 + with: + path: | + ~/.julia + !~/.julia/registries + key: ${{ runner.os }}-julia-${{ env.CACHE_NUM }}-${{ steps.read_toml.outputs.value }}-${{ needs.setup.outputs.hash }} + restore-keys: | + ${{ runner.os }}-julia-${{ env.CACHE_NUM }}-${{ steps.read_toml.outputs.value }}- - name: Execute notebook if: ${{ steps.nb-cache.outputs.cache-hit != 'true' }} - run: > - docker run -w /tmp -v ${{ github.workspace }}:/tmp - -e NB=${{ env.NB }} - -e NBCACHE=${{ env.NBCACHE }} - -e WORKSPACE=${{ github.workspace }} - -e ALLOWERRORS=${{ env.ALLOWERRORS }} - ${{ env.IMG }} - julia execute.jl - - name: Claim output cache folder - run: | - sudo chown -R $USER ${{ env.NBCACHE }} - ls -R ${{ env.NBCACHE }} + run: julia execute.jl - name: Cache notebook uses: actions/cache/save@v4 if: ${{ steps.nb-cache.outputs.cache-hit != 'true' }} @@ -122,7 +164,6 @@ jobs: run: cp --verbose -rf ${{ env.NBCACHE }}/docs/* docs/ - name: Setup Python uses: actions/setup-python@v5 - id: setup-python with: python-version: '3.x' - name: Install uv diff --git a/.github/workflows/linkcheck.yml b/.github/workflows/linkcheck.yml index f86193d..4931b04 100644 --- a/.github/workflows/linkcheck.yml +++ b/.github/workflows/linkcheck.yml @@ -49,6 +49,6 @@ jobs: - name: Install uv run: curl -LsSf https://astral.sh/uv/install.sh | sh - name: Install Python dependencies - run: uv pip install -r requirements-jb.txt + run: uv pip install -r requirements.txt - name: Build website run: jupyter-book build ${DIR} --builder linkcheck diff --git a/.github/workflows/rm-old-image.yml b/.github/workflows/rm-old-image.yml deleted file mode 100644 index e053549..0000000 --- a/.github/workflows/rm-old-image.yml +++ /dev/null @@ -1,20 +0,0 @@ -name: Docker image retention -on: - workflow_dispatch: - schedule: - - cron: '0 0 * * 1' # Every week - -jobs: - clean: - name: Delete old images - runs-on: ubuntu-latest - permissions: - packages: write - steps: - - uses: snok/container-retention-policy@v3.0.0 - with: - account: user - token: ${{ secrets.GITHUB_TOKEN }} - image-names: ${{ github.event.repository.name }} - cut-off: 2w - dry-run: false diff --git a/.github/workflows/update-manifest.yml b/.github/workflows/update-manifest.yml index d497c74..bd20659 100644 --- a/.github/workflows/update-manifest.yml +++ b/.github/workflows/update-manifest.yml @@ -26,13 +26,14 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 + - name: Setup Julia + uses: julia-actions/setup-julia@v2 + with: + version: '1' - name: Update Julia dependencies - run: > - docker run --rm -w /tmp - -v ${{ github.workspace }}:/tmp - -e JULIA_PKG_PRECOMPILE_AUTO=0 - julia:latest - julia --project=@. --color=yes -e 'import Pkg; Pkg.update()' + env: + JULIA_PKG_PRECOMPILE_AUTO: '0' + run: julia --project=@. --color=yes -e 'import Pkg; Pkg.update()' # Authenticate with a custom GitHub APP # https://github.com/peter-evans/create-pull-request/blob/main/docs/concepts-guidelines.md#authenticating-with-github-app-generated-tokens - name: Generate token for PR diff --git a/env.Dockerfile b/env.Dockerfile deleted file mode 100644 index 7fe641b..0000000 --- a/env.Dockerfile +++ /dev/null @@ -1,27 +0,0 @@ -FROM julia:1.10.5 as julia -FROM ghcr.io/astral-sh/uv:latest as uv -FROM python:3.12.5-slim - -# System config -ENV UV_SYSTEM_PYTHON '1' -ENV JULIA_CI 'true' -ENV JULIA_NUM_THREADS 'auto' -# Let PythonCall use built-in python -ENV JULIA_CONDAPKG_BACKEND 'Null' -ENV JULIA_PATH '/usr/local/julia/' -ENV JULIA_DEPOT_PATH '/srv/juliapkg/' -ENV PATH ${JULIA_PATH}/bin:${PATH} -COPY --from=julia ${JULIA_PATH} ${JULIA_PATH} -COPY --from=uv /uv /bin/uv - -WORKDIR /app - -# Python dependencies -COPY requirements.txt . -RUN uv pip install --no-cache -r requirements.txt - -# Julia dependencies -COPY Project.toml Manifest.toml ./ -COPY src/ src -RUN julia --color=yes -e 'using Pkg; Pkg.add(["IJulia", "Literate", "JSON"]); import IJulia; IJulia.installkernel("Julia", "--project=@.")' && \ - julia --color=yes --project=@. -e 'using Pkg; Pkg.instantiate(); Pkg.precompile()' diff --git a/execute.jl b/execute.jl index 07d2121..49481d4 100644 --- a/execute.jl +++ b/execute.jl @@ -1,16 +1,17 @@ using Literate using JSON using Pkg +using IJulia Pkg.activate(Base.current_project()) ENV["GKSwstype"] = "100" -file = get(ENV, "NB", "test.ipynb") -cachedir = get(ENV, "NBCACHE", ".cache") function main(; rmsvg=true) + file = get(ENV, "NB", "test.ipynb") if endswith(file, ".jl") run_literate(file; rmsvg) elseif endswith(file, ".ipynb") + IJulia.installkernel("Julia", "--project=@.") run_ipynb(file) else error("$(file) is not a valid notebook file!") @@ -40,6 +41,7 @@ function strip_svg(ipynb) end function run_literate(file; rmsvg=true) + cachedir = get(ENV, "NBCACHE", ".cache") outpath = joinpath(abspath(pwd()), cachedir, dirname(file)) mkpath(outpath) ipynb = Literate.notebook(file, outpath; mdstrings=true, execute=true) @@ -48,6 +50,7 @@ function run_literate(file; rmsvg=true) end function run_ipynb(file) + cachedir = get(ENV, "NBCACHE", ".cache") outpath = joinpath(abspath(pwd()), cachedir, file) mkpath(dirname(outpath)) kernelname = "--ExecutePreprocessor.kernel_name=julia-1.$(VERSION.minor)"