diff --git a/.ci/create-changes-html.sh b/.ci/create-changes-html.sh index 40322ca86b9..2dc3ca61a41 100755 --- a/.ci/create-changes-html.sh +++ b/.ci/create-changes-html.sh @@ -51,7 +51,7 @@ diffParagraphs.forEach(paragraph => { EOF echo '' >> CHANGES.html echo '
' >> CHANGES.html -(cd $DOC_REPOSITORY && git diff $BASE_DOC_COMMIT -- *.html) > diff.txt +(cd $DOC_REPOSITORY && git diff $BASE_DOC_COMMIT -- "*.html") > diff.txt python3 - << EOF import os, re, html with open('diff.txt', 'r') as f: diff --git a/.ci/docker-exec-script.sh b/.ci/docker-exec-script.sh new file mode 100755 index 00000000000..60bbd70aee7 --- /dev/null +++ b/.ci/docker-exec-script.sh @@ -0,0 +1,14 @@ +#!/bin/sh -x +if [ $# -lt 3 ]; then + echo >&2 "usage: docker-exec-script.sh CONTAINER WORKDIR [VAR=VALUE...] SCRIPT" + exit 1 +fi +CONTAINER=$1 +WORKDIR=$2 +shift 2 +(echo "cd \"$WORKDIR\""; + while [ $# -gt 1 ]; do + echo "export \"$1\"" + shift + done; + cat "$1") | docker exec -i $CONTAINER bash -ex diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3eecd1a4666..e9703d30bfa 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -26,192 +26,353 @@ concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true +# +# The three workflows: +# +# - build.yml (with jobs test-new, test-mod, test-long), +# - doc-build.yml, +# - doc-build-pdf.yml +# +# each build Sage: +# +# - incrementally starting from a Docker image that ci-linux.yml +# publishes on each development release to ghcr.io, +# - orchestrated using a tox-generated Dockerfile, +# - using https://github.com/marketplace/actions/build-and-push-docker-images, +# - pushing the image to a local registry, +# - then executing a container loaded from that registry, +# +# and then run various tests or build the documentation. +# +# Without the use of a cache, this runs the same incremental rebuild of Sage +# multiple times; there is no interaction between these workflow and jobs. +# +# This baseline is transparently improved by our use of the GH Actions cache, +# see https://docs.docker.com/build/ci/github-actions/cache/#cache-backend-api. +# +# Jobs test-mod and test-long are only started after test-new completed; +# but the workflows doc-build.yml and doc-build-pdf.yml are started independently. +# +# - When nothing is cached and the 3 workflows are launched in parallel, +# they may each run the same incremental rebuild. +# +# - When there's congestion that leads to the workflows to be run serially, +# the 2nd and 3rd workflow will be able to use the cache from the 1st workflow. +# +# This elasticity may be helpful in reducing congestion. +# +# There is a rather small per-project limit of 10 GB for this cache. +# Use https://github.com/sagemath/sage/actions/caches to monitor the utilization +# of the cache. +# + +env: + # Adapted from docker.yml + TOX_ENV: "docker-${{ github.event.inputs.platform || 'ubuntu-jammy-standard' }}-incremental" + BUILD_IMAGE: "localhost:5000/${{ github.repository }}/sage-${{ github.event.inputs.platform || 'ubuntu-jammy-standard' }}-with-targets:ci" + FROM_DOCKER_REPOSITORY: "ghcr.io/sagemath/sage/" + FROM_DOCKER_TARGET: "with-targets" + FROM_DOCKER_TAG: ${{ github.event.inputs.docker_tag || 'dev'}} + EXTRA_CONFIGURE_ARGS: --enable-fat-binary + jobs: - get_ci_fixes: + test-new: runs-on: ubuntu-latest + services: + # https://docs.docker.com/build/ci/github-actions/local-registry/ + registry: + image: registry:2 + ports: + - 5000:5000 steps: + - name: Maximize build disk space + uses: easimon/maximize-build-space@v10 + with: + # need space in /var for Docker images + root-reserve-mb: 30000 + remove-dotnet: true + remove-android: true + remove-haskell: true + remove-codeql: true + remove-docker-images: true - name: Checkout id: checkout uses: actions/checkout@v4 + - name: Install test prerequisites + # From docker.yml + run: | + sudo DEBIAN_FRONTEND=noninteractive apt-get update + sudo DEBIAN_FRONTEND=noninteractive apt-get install tox + sudo apt-get clean + df -h - name: Merge CI fixes from sagemath/sage + # From docker.yml + # This step needs to happen after the commit sha is put in DOCKER_TAG + # so that multi-stage builds can work correctly. run: | mkdir -p upstream .ci/merge-fixes.sh 2>&1 | tee upstream/ci_fixes.log env: GH_TOKEN: ${{ github.token }} SAGE_CI_FIXES_FROM_REPOSITORIES: ${{ vars.SAGE_CI_FIXES_FROM_REPOSITORIES }} - - name: Store CI fixes in upstream artifact - run: | - if git format-patch --stdout test_base > ci_fixes.patch; then - cp ci_fixes.patch upstream/ - fi - - uses: actions/upload-artifact@v3 - with: - path: upstream - name: upstream - build: - runs-on: ubuntu-latest - container: ghcr.io/sagemath/sage/sage-${{ github.event.inputs.platform || 'ubuntu-jammy-standard' }}-with-targets:${{ github.event.inputs.docker_tag || 'dev'}} - needs: [get_ci_fixes] - steps: - - name: Checkout - id: checkout - uses: actions/checkout@v4 + # Building - - name: Update system packages - id: prepare + - name: Generate Dockerfile + # From docker.yml run: | - export PATH="build/bin:$PATH" - eval $(sage-print-system-package-command auto update) - eval $(sage-print-system-package-command auto --spkg --yes --no-install-recommends install git) + tox -e ${{ env.TOX_ENV }} + cp .tox/${{ env.TOX_ENV }}/Dockerfile . + env: + # Only generate the Dockerfile, do not run 'docker build' here + DOCKER_TARGETS: "" - - name: Add prebuilt tree as a worktree - id: worktree - run: | - set -ex - git config --global --add safe.directory $(pwd) - .ci/retrofit-worktree.sh worktree-image /sage + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + with: + driver-opts: network=host - - name: Download upstream artifact - uses: actions/download-artifact@v3 + - name: Build Docker image + id: image + uses: docker/build-push-action@v5 with: - path: upstream - name: upstream + # push and load may not be set together at the moment + # + # We are using "push" (to the local registry) because it was + # more reliable than "load", for which we observed random failure + # conditions in which the built image could not be found. + # + push: true + load: false + context: . + tags: ${{ env.BUILD_IMAGE }} + target: with-targets + cache-from: type=gha + cache-to: type=gha,mode=max + build-args: | + NUMPROC=6 + USE_MAKEFLAGS=-k V=0 SAGE_NUM_THREADS=4 --output-sync=recurse + TARGETS_PRE=build/make/Makefile + TARGETS=ci-build-with-fallback - - name: Apply CI fixes from sagemath/sage - # After applying the fixes, make sure all changes are marked as uncommitted changes. + - name: Start container run: | - if [ -r upstream/ci_fixes.patch ]; then - (cd worktree-image && git commit -q -m "current changes" --allow-empty -a && git am; git reset --quiet old; git add -N .) < upstream/ci_fixes.patch - fi + docker run --name BUILD -dit \ + --mount type=bind,src=$(pwd),dst=$(pwd) \ + --workdir $(pwd) \ + ${{ env.BUILD_IMAGE }} /bin/sh - - name: Incremental build - id: incremental + # Testing + + - name: Check that all modules can be imported run: | - # Now re-bootstrap and build. The build is incremental because we were careful with the timestamps. - ./bootstrap && make build - working-directory: ./worktree-image - env: - MAKE: make -j4 --output-sync=recurse - SAGE_NUM_THREADS: 4 + # Increase the length of the lines in the "short summary" + export COLUMNS=120 + # The following command checks that all modules can be imported. + # The output also includes a long list of modules together with the number of tests in each module. + # This can be ignored. + ./sage -python -m pip install pytest-xdist + ./sage -python -m pytest -c tox.ini -qq --doctest --collect-only || true + shell: sh .ci/docker-exec-script.sh BUILD /sage {0} - - name: Build modularized distributions - if: (success() || failure()) && steps.worktree.outcome == 'success' - run: make V=0 tox && make SAGE_CHECK=no pypi-wheels - working-directory: ./worktree-image - env: - MAKE: make -j4 --output-sync=recurse - SAGE_NUM_THREADS: 4 + - name: Test changed files (sage -t --new) + run: | + export MAKE="make -j2 --output-sync=recurse" SAGE_NUM_THREADS=4 + # We run tests with "sage -t --new"; this only tests the uncommitted changes. + ./sage -t --new -p4 + shell: sh .ci/docker-exec-script.sh BUILD /sage {0} - - name: Static code check with pyright - if: (success() || failure()) && steps.worktree.outcome == 'success' - uses: jakebailey/pyright-action@v1 - with: - version: 1.1.332 - # Many warnings issued by pyright are not yet helpful because there is not yet enough type information. - no-comments: true - working-directory: ./worktree-image - env: - # To avoid out of memory errors - NODE_OPTIONS: --max-old-space-size=8192 - - - name: Static code check with pyright (annotated) - if: (success() || failure()) && steps.worktree.outcome == 'success' - uses: jakebailey/pyright-action@v1 + test-mod: + runs-on: ubuntu-latest + needs: [test-new] + services: + # https://docs.docker.com/build/ci/github-actions/local-registry/ + registry: + image: registry:2 + ports: + - 5000:5000 + strategy: + fail-fast: false + matrix: + targets: + - sagemath_categories-check + steps: + - name: Maximize build disk space + uses: easimon/maximize-build-space@v10 with: - version: 1.1.332 - # Issue errors - no-comments: false - level: error - working-directory: ./worktree-image + # need space in /var for Docker images + root-reserve-mb: 30000 + remove-dotnet: true + remove-android: true + remove-haskell: true + remove-codeql: true + remove-docker-images: true + - name: Checkout + id: checkout + uses: actions/checkout@v4 + - name: Install test prerequisites + # From docker.yml + run: | + sudo DEBIAN_FRONTEND=noninteractive apt-get update + sudo DEBIAN_FRONTEND=noninteractive apt-get install tox + sudo apt-get clean + df -h + - name: Merge CI fixes from sagemath/sage + # From docker.yml + # This step needs to happen after the commit sha is put in DOCKER_TAG + # so that multi-stage builds can work correctly. + run: | + .ci/merge-fixes.sh env: - # To avoid out of memory errors - NODE_OPTIONS: --max-old-space-size=8192 + GH_TOKEN: ${{ github.token }} + + # Building - - name: Clean (fallback to non-incremental) - id: clean - if: (success() || failure()) && steps.worktree.outcome == 'success' && steps.incremental.outcome != 'success' + - name: Generate Dockerfile + # From docker.yml run: | - set -ex - ./bootstrap && make doc-clean doc-uninstall sagelib-clean && git clean -fx src/sage && ./config.status - working-directory: ./worktree-image + tox -e ${{ env.TOX_ENV }} + cp .tox/${{ env.TOX_ENV }}/Dockerfile . env: - MAKE: make -j4 - SAGE_NUM_THREADS: 4 + # Only generate the Dockerfile, do not run 'docker build' here + DOCKER_TARGETS: "" - - name: Build - # This step is needed because building the modularized distributions installs some optional packages, - # so the editable install of sagelib needs to build the corresponding optional extension modules. - id: build - if: (success() || failure()) && (steps.incremental.outcome == 'success' || steps.clean.outcome == 'success') + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + with: + driver-opts: network=host + + - name: Build Docker image + id: image + uses: docker/build-push-action@v5 + with: + push: true + load: false + context: . + tags: ${{ env.BUILD_IMAGE }} + target: with-targets + cache-from: type=gha + cache-to: type=gha,mode=max + build-args: | + NUMPROC=6 + USE_MAKEFLAGS=-k V=0 SAGE_NUM_THREADS=4 --output-sync=recurse + TARGETS_PRE=build/make/Makefile + TARGETS=ci-build-with-fallback + + - name: Start container run: | - make build - working-directory: ./worktree-image - env: - MAKE: make -j4 --output-sync=recurse - SAGE_NUM_THREADS: 4 + docker run --name BUILD -dit \ + --mount type=bind,src=$(pwd),dst=$(pwd) \ + --workdir $(pwd) \ + ${{ env.BUILD_IMAGE }} /bin/sh # Testing - - name: Test changed files (sage -t --new) - if: (success() || failure()) && steps.build.outcome == 'success' + - name: Test modularized distributions run: | - # We run tests with "sage -t --new"; this only tests the uncommitted changes. - ./sage -t --new -p2 - working-directory: ./worktree-image - env: - MAKE: make -j4 --output-sync=recurse - SAGE_NUM_THREADS: 4 + export MAKE="make -j2 --output-sync=recurse" SAGE_NUM_THREADS=4 + make V=0 tox-ensure && make ${{ matrix.targets }} + shell: sh .ci/docker-exec-script.sh BUILD /sage {0} - - name: Test modularized distributions - if: (success() || failure()) && steps.build.outcome == 'success' - run: make V=0 tox && make pypi-wheels-check - working-directory: ./worktree-image + test-long: + runs-on: ubuntu-latest + needs: [test-new] + services: + # https://docs.docker.com/build/ci/github-actions/local-registry/ + registry: + image: registry:2 + ports: + - 5000:5000 + steps: + - name: Maximize build disk space + uses: easimon/maximize-build-space@v10 + with: + # need space in /var for Docker images + root-reserve-mb: 30000 + remove-dotnet: true + remove-android: true + remove-haskell: true + remove-codeql: true + remove-docker-images: true + - name: Checkout + id: checkout + uses: actions/checkout@v4 + - name: Install test prerequisites + # From docker.yml + run: | + sudo DEBIAN_FRONTEND=noninteractive apt-get update + sudo DEBIAN_FRONTEND=noninteractive apt-get install tox + sudo apt-get clean + df -h + - name: Merge CI fixes from sagemath/sage + # From docker.yml + # This step needs to happen after the commit sha is put in DOCKER_TAG + # so that multi-stage builds can work correctly. + run: | + .ci/merge-fixes.sh env: - MAKE: make -j4 --output-sync=recurse - SAGE_NUM_THREADS: 4 + GH_TOKEN: ${{ github.token }} - - name: Check that all modules can be imported + # Building + + - name: Generate Dockerfile + # From docker.yml run: | - # The following command checks that all modules can be imported. - # The output also includes a long list of modules together with the number of tests in each module. - # This can be ignored. - ../sage -python -m pip install pytest-xdist - ../sage -python -m pytest -c tox.ini -qq --doctest --collect-only || true - working-directory: ./worktree-image/src + tox -e ${{ env.TOX_ENV }} + cp .tox/${{ env.TOX_ENV }}/Dockerfile . env: - # Increase the length of the lines in the "short summary" - COLUMNS: 120 + # Only generate the Dockerfile, do not run 'docker build' here + DOCKER_TARGETS: "" + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + with: + driver-opts: network=host + + - name: Build Docker image + id: image + uses: docker/build-push-action@v5 + with: + push: true + load: false + context: . + tags: ${{ env.BUILD_IMAGE }} + target: with-targets + cache-from: type=gha + cache-to: type=gha,mode=max + build-args: | + NUMPROC=6 + USE_MAKEFLAGS=-k V=0 SAGE_NUM_THREADS=4 --output-sync=recurse + TARGETS_PRE=build/make/Makefile + TARGETS=ci-build-with-fallback - - name: Pytest - if: contains(github.ref, 'pytest') + - name: Start container + id: container run: | - ../sage -python -m pip install coverage pytest-xdist - ../sage -python -m coverage run -m pytest -c tox.ini --doctest || true - working-directory: ./worktree-image/src - env: - # Increase the length of the lines in the "short summary" - COLUMNS: 120 + docker run --name BUILD -dit \ + --mount type=bind,src=$(pwd),dst=$(pwd) \ + --workdir $(pwd) \ + ${{ env.BUILD_IMAGE }} /bin/sh + + # Testing - name: Test all files (sage -t --all --long) - if: (success() || failure()) && steps.build.outcome == 'success' run: | ./sage -python -m pip install coverage - ./sage -python -m coverage run --rcfile=src/tox.ini src/bin/sage-runtests --all --long -p2 --format github --random-seed=286735480429121101562228604801325644303 - working-directory: ./worktree-image + ./sage -python -m coverage run --rcfile=src/tox.ini src/bin/sage-runtests --all --long -p4 --format github --random-seed=286735480429121101562228604801325644303 + shell: sh .ci/docker-exec-script.sh BUILD /sage {0} - - name: Prepare coverage results - if: (success() || failure()) && steps.build.outcome == 'success' + - name: Copy coverage results + if: (success() || failure()) && steps.container.outcome == 'success' run: | ./sage -python -m coverage combine --rcfile=src/tox.ini ./sage -python -m coverage xml --rcfile=src/tox.ini mkdir -p coverage-report mv coverage.xml coverage-report/ - working-directory: ./worktree-image + shell: sh .ci/docker-exec-script.sh BUILD /sage {0} - name: Upload coverage to codecov - if: (success() || failure()) && steps.build.outcome == 'success' + if: (success() || failure()) && steps.container.outcome == 'success' uses: codecov/codecov-action@v3 with: - directory: ./worktree-image/coverage-report + directory: ./coverage-report diff --git a/.github/workflows/ci-linux-incremental.yml b/.github/workflows/ci-linux-incremental.yml index 82b14472c55..4861ef1d1d0 100644 --- a/.github/workflows/ci-linux-incremental.yml +++ b/.github/workflows/ci-linux-incremental.yml @@ -19,7 +19,12 @@ on: pull_request: paths: - 'build/pkgs/**' - - 'pkgs/**' + - '!build/pkgs/sage_conf/**' + - '!build/pkgs/sage_docbuild/**' + - '!build/pkgs/sage_setup/**' + - '!build/pkgs/sage_sws2rst/**' + - '!build/pkgs/sagelib/**' + - '!build/pkgs/sagemath_*/**' workflow_dispatch: concurrency: diff --git a/.github/workflows/doc-build-pdf.yml b/.github/workflows/doc-build-pdf.yml index ee90f3a42fc..983455cacb0 100644 --- a/.github/workflows/doc-build-pdf.yml +++ b/.github/workflows/doc-build-pdf.yml @@ -20,13 +20,44 @@ concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true +env: + # Same as in build.yml + TOX_ENV: "docker-${{ github.event.inputs.platform || 'ubuntu-jammy-standard' }}-incremental" + BUILD_IMAGE: "localhost:5000/${{ github.repository }}/sage-${{ github.event.inputs.platform || 'ubuntu-jammy-standard' }}-with-targets:ci" + FROM_DOCKER_REPOSITORY: "ghcr.io/sagemath/sage/" + FROM_DOCKER_TARGET: "with-targets" + FROM_DOCKER_TAG: ${{ github.event.inputs.docker_tag || 'dev'}} + EXTRA_CONFIGURE_ARGS: --enable-fat-binary + jobs: - get_ci_fixes: + build-docs-pdf: runs-on: ubuntu-latest + services: + # https://docs.docker.com/build/ci/github-actions/local-registry/ + registry: + image: registry:2 + ports: + - 5000:5000 steps: + - name: Maximize build disk space + uses: easimon/maximize-build-space@v8 + with: + # need space in /var for Docker images + root-reserve-mb: 30000 + remove-dotnet: true + remove-android: true + remove-haskell: true + remove-codeql: true + remove-docker-images: true - name: Checkout - id: checkout uses: actions/checkout@v4 + - name: Install test prerequisites + # From docker.yml + run: | + sudo DEBIAN_FRONTEND=noninteractive apt-get update + sudo DEBIAN_FRONTEND=noninteractive apt-get install tox + sudo apt-get clean + df -h - name: Merge CI fixes from sagemath/sage run: | mkdir -p upstream @@ -34,82 +65,64 @@ jobs: env: GH_TOKEN: ${{ github.token }} SAGE_CI_FIXES_FROM_REPOSITORIES: ${{ vars.SAGE_CI_FIXES_FROM_REPOSITORIES }} - - name: Store CI fixes in upstream artifact - run: | - if git format-patch --stdout test_base > ci_fixes.patch; then - cp ci_fixes.patch upstream/ - fi - - uses: actions/upload-artifact@v3 - with: - path: upstream - name: upstream - build-docs-pdf: - runs-on: ubuntu-latest - container: ghcr.io/sagemath/sage/sage-${{ github.event.inputs.platform || 'ubuntu-jammy-standard' }}-with-targets:${{ github.event.inputs.docker_tag || 'dev'}} - needs: [get_ci_fixes] - steps: - - name: Checkout - uses: actions/checkout@v4 + # Building - - name: Update system packages + - name: Generate Dockerfile + # From docker.yml run: | - export PATH="build/bin:$PATH" - eval $(sage-print-system-package-command auto update) - eval $(sage-print-system-package-command auto --yes --no-install-recommends install zip) - eval $(sage-print-system-package-command auto --spkg --yes --no-install-recommends install git texlive texlive_luatex free_fonts xindy) + tox -e ${{ env.TOX_ENV }} + cp .tox/${{ env.TOX_ENV }}/Dockerfile . + env: + # Only generate the Dockerfile, do not run 'docker build' here + DOCKER_TARGETS: "" - - name: Add prebuilt tree as a worktree - id: worktree - run: | - git config --global --add safe.directory $(pwd) - git config --global user.email "ci-sage@example.com" - git config --global user.name "Build & Test workflow" - .ci/retrofit-worktree.sh worktree-image /sage + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + with: + driver-opts: network=host - - name: Download upstream artifact - uses: actions/download-artifact@v3 + - name: Build Docker image + id: image + uses: docker/build-push-action@v5 with: - path: upstream - name: upstream + # push and load may not be set together at the moment + push: true + load: false + context: . + tags: ${{ env.BUILD_IMAGE }} + target: with-targets + cache-from: type=gha + cache-to: type=gha,mode=max + build-args: | + NUMPROC=6 + USE_MAKEFLAGS=-k V=0 SAGE_NUM_THREADS=4 --output-sync=recurse + TARGETS_PRE=build/make/Makefile + TARGETS=ci-build-with-fallback - - name: Apply CI fixes from sagemath/sage - # After applying the fixes, make sure all changes are marked as uncommitted changes. + - name: Start container run: | - if [ -r upstream/ci_fixes.patch ]; then - (cd worktree-image && git commit -q -m "current changes" --allow-empty -a && git am; git reset --quiet old; git add -N .) < upstream/ci_fixes.patch - fi + docker run --name BUILD -dit \ + --mount type=bind,src=$(pwd),dst=$(pwd) \ + --workdir $(pwd) \ + ${{ env.BUILD_IMAGE }} /bin/sh - - name: Incremental build - id: incremental - run: | - # Now re-bootstrap and build. The build is incremental because we were careful with the timestamps. - ./bootstrap && make build - working-directory: ./worktree-image - env: - MAKE: make -j4 --output-sync=recurse - SAGE_NUM_THREADS: 4 + # Docs - - name: Build (fallback to non-incremental) - id: build - if: (success() || failure()) && steps.worktree.outcome == 'success' && steps.incremental.outcome != 'success' + - name: Update system packages run: | - set -ex - make sagelib-clean && git clean -fx src/sage && ./config.status && make build - working-directory: ./worktree-image - env: - MAKE: make -j4 --output-sync=recurse - SAGE_NUM_THREADS: 4 + export PATH="build/bin:$PATH" + eval $(sage-print-system-package-command auto update) + eval $(sage-print-system-package-command auto --yes --no-install-recommends install zip) + eval $(sage-print-system-package-command auto --spkg --yes --no-install-recommends install git texlive texlive_luatex free_fonts xindy) + shell: sh .ci/docker-exec-script.sh BUILD /sage {0} - name: Build docs (PDF) id: docbuild - if: (success() || failure()) && (steps.incremental.outcome == 'success' || steps.build.outcome == 'success') run: | + export MAKE="make -j5 --output-sync=recurse" SAGE_NUM_THREADS=5 make doc-clean doc-uninstall; make sagemath_doc_html-build-deps sagemath_doc_pdf-no-deps - working-directory: ./worktree-image - env: - MAKE: make -j4 --output-sync=recurse - SAGE_NUM_THREADS: 4 + shell: sh .ci/docker-exec-script.sh BUILD /sage {0} - name: Copy docs id: copy @@ -121,6 +134,7 @@ jobs: cp -r -L /sage/local/share/doc/sage/pdf ./docs # Zip everything for increased performance zip -r docs-pdf.zip docs + shell: sh .ci/docker-exec-script.sh BUILD /sage {0} - name: Upload docs if: (success() || failure()) && steps.copy.outcome == 'success' diff --git a/.github/workflows/doc-build.yml b/.github/workflows/doc-build.yml index b3c275d8e0a..a9a1ddece13 100644 --- a/.github/workflows/doc-build.yml +++ b/.github/workflows/doc-build.yml @@ -9,19 +9,59 @@ on: - develop workflow_dispatch: # Allow to run manually + inputs: + platform: + description: 'Platform' + required: true + default: 'ubuntu-jammy-standard' + docker_tag: + description: 'Docker tag' + required: true + default: 'dev' concurrency: # Cancel previous runs of this workflow for the same branch group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true +env: + # Same as in build.yml + TOX_ENV: "docker-${{ github.event.inputs.platform || 'ubuntu-jammy-standard' }}-incremental" + BUILD_IMAGE: "localhost:5000/${{ github.repository }}/sage-${{ github.event.inputs.platform || 'ubuntu-jammy-standard' }}-with-targets:ci" + FROM_DOCKER_REPOSITORY: "ghcr.io/sagemath/sage/" + FROM_DOCKER_TARGET: "with-targets" + FROM_DOCKER_TAG: ${{ github.event.inputs.docker_tag || 'dev'}} + EXTRA_CONFIGURE_ARGS: --enable-fat-binary + jobs: - get_ci_fixes: + build-docs: runs-on: ubuntu-latest + services: + # https://docs.docker.com/build/ci/github-actions/local-registry/ + registry: + image: registry:2 + ports: + - 5000:5000 steps: + - name: Maximize build disk space + uses: easimon/maximize-build-space@v8 + with: + # need space in /var for Docker images + root-reserve-mb: 30000 + remove-dotnet: true + remove-android: true + remove-haskell: true + remove-codeql: true + remove-docker-images: true - name: Checkout - id: checkout uses: actions/checkout@v4 + - name: Install test prerequisites + # From docker.yml + run: | + sudo DEBIAN_FRONTEND=noninteractive apt-get update + sudo DEBIAN_FRONTEND=noninteractive apt-get install tox + sudo apt-get clean + df -h - name: Merge CI fixes from sagemath/sage run: | mkdir -p upstream @@ -29,120 +69,101 @@ jobs: env: GH_TOKEN: ${{ github.token }} SAGE_CI_FIXES_FROM_REPOSITORIES: ${{ vars.SAGE_CI_FIXES_FROM_REPOSITORIES }} - - name: Store CI fixes in upstream artifact + + # Building + + - name: Generate Dockerfile + # From docker.yml run: | - if git format-patch --stdout test_base > ci_fixes.patch; then - cp ci_fixes.patch upstream/ - fi - - uses: actions/upload-artifact@v3 - with: - path: upstream - name: upstream + tox -e ${{ env.TOX_ENV }} + cp .tox/${{ env.TOX_ENV }}/Dockerfile . + env: + # Only generate the Dockerfile, do not run 'docker build' here + DOCKER_TARGETS: "" - build-docs: - runs-on: ubuntu-latest - container: ghcr.io/sagemath/sage/sage-ubuntu-jammy-standard-with-targets:dev - needs: [get_ci_fixes] - steps: - - name: Checkout - uses: actions/checkout@v4 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + with: + driver-opts: network=host - - name: Update system packages + - name: Build Docker image + id: image + uses: docker/build-push-action@v5 + with: + # push and load may not be set together at the moment + push: true + load: false + context: . + tags: ${{ env.BUILD_IMAGE }} + target: with-targets + cache-from: type=gha + cache-to: type=gha,mode=max + build-args: | + NUMPROC=6 + USE_MAKEFLAGS=-k V=0 SAGE_NUM_THREADS=4 --output-sync=recurse + TARGETS_PRE=build/make/Makefile + TARGETS=ci-build-with-fallback + + - name: Start container run: | - apt-get update && apt-get install -y git zip + docker run --name BUILD -dit \ + --mount type=bind,src=$(pwd),dst=$(pwd) \ + --workdir $(pwd) \ + ${{ env.BUILD_IMAGE }} /bin/sh + + # Docs - - name: Add prebuilt tree as a worktree + - name: Store old docs id: worktree run: | git config --global --add safe.directory $(pwd) git config --global user.email "ci-sage@example.com" - git config --global user.name "Build & Test workflow" - # mathjax path in old doc - mathjax_path_from=$(SAGE_USE_CDNS=no /sage/sage -python -c "from sage_docbuild.conf import mathjax_path; print(mathjax_path)") - .ci/retrofit-worktree.sh worktree-image /sage + git config --global user.name "Build documentation workflow" + # mathjax path in old doc (regex) + mathjax_path_from="[-./A-Za-z_]*/tex-chtml[.]js?v=[0-9a-f]*" # mathjax path in new doc - mathjax_path_to=$(SAGE_USE_CDNS=yes /sage/sage -python -c "from sage_docbuild.conf import mathjax_path; print(mathjax_path)") - new_version=$(cat src/VERSION.txt) + mathjax_path_to=$(docker exec -e SAGE_USE_CDNS=yes BUILD /sage/sage -python -c "from sage_docbuild.conf import mathjax_path; print(mathjax_path)") + new_version=$(docker exec BUILD cat src/VERSION.txt) + mkdir -p docs/ + docker cp BUILD:/sage/local/share/doc/sage/html docs/ # Wipe out chronic diffs between old doc and new doc - (cd /sage/local/share/doc/sage/html && \ + (cd docs && \ find . -name "*.html" | xargs sed -i -e '/class="sidebar-brand-text"/ s/Sage [0-9a-z.]* /Sage '"$new_version"' /' \ -e 's;'"$mathjax_path_from"';'"$mathjax_path_to"';' \ -e '\;; d') # Create git repo from old doc - DOC_DIR=/sage/local/share/doc/sage/html - (cd $DOC_DIR && git init && \ + (cd docs && \ + git init && \ (echo "*.svg binary"; echo "*.pdf binary") >> .gitattributes && \ (echo ".buildinfo"; echo '*.inv'; echo '.git*'; echo '*.svg'; echo '*.pdf'; echo '*.png'; echo 'searchindex.js') > .gitignore; \ git add -A && git commit --quiet -m "old") - - name: Download upstream artifact - uses: actions/download-artifact@v3 - with: - path: upstream - name: upstream - - - name: Apply CI fixes from sagemath/sage - # After applying the fixes, make sure all changes are marked as uncommitted changes. - run: | - if [ -r upstream/ci_fixes.patch ]; then - (cd worktree-image && git commit -q -m "current changes" --allow-empty -a && git am; git reset --quiet old; git add -N .) < upstream/ci_fixes.patch - fi - - - name: Incremental build - id: incremental - run: | - # Now re-bootstrap and build. The build is incremental because we were careful with the timestamps. - ./bootstrap && make sagemath_doc_html-build-deps - working-directory: ./worktree-image - env: - MAKE: make -j4 --output-sync=recurse - SAGE_NUM_THREADS: 4 - - - name: Build (fallback to non-incremental) - id: build - if: (success() || failure()) && steps.worktree.outcome == 'success' && steps.incremental.outcome != 'success' - run: | - set -ex - make sagelib-clean && git clean -fx src/sage && ./config.status && make sagemath_doc_html-build-deps - working-directory: ./worktree-image - env: - MAKE: make -j4 --output-sync=recurse - SAGE_NUM_THREADS: 4 - - name: Build docs id: docbuild - if: (success() || failure()) && (steps.incremental.outcome == 'success' || steps.build.outcome == 'success') # Always non-incremental because of the concern that # incremental docbuild may introduce broken links (inter-file references) though build succeeds run: | - set -ex - DOC_DIR=/sage/local/share/doc/sage/html - mv $DOC_DIR/.git /sage/.git-doc + export MAKE="make -j5 --output-sync=recurse" SAGE_NUM_THREADS=5 make doc-clean doc-uninstall - mkdir -p $DOC_DIR/ && mv /sage/.git-doc $DOC_DIR/.git export SAGE_USE_CDNS=yes ./config.status && make sagemath_doc_html-no-deps - working-directory: ./worktree-image - env: - MAKE: make -j4 --output-sync=recurse - SAGE_NUM_THREADS: 4 + shell: sh .ci/docker-exec-script.sh BUILD /sage {0} - name: Copy docs id: copy if: (success() || failure()) && steps.docbuild.outcome == 'success' run: | set -ex - DOC_DIR=/sage/local/share/doc/sage/html - (cd $DOC_DIR && git commit -a -m 'new') - ls -l /sage/venv/bin - PATH=/sage/venv/bin:$PATH .ci/create-changes-html.sh $(cd $DOC_DIR; git rev-parse HEAD^) $DOC_DIR - (cd $DOC_DIR && rm -rf .git) # We copy everything to a local folder + docker cp BUILD:/sage/local/share/doc/sage/html docs + docker cp BUILD:/sage/local/share/doc/sage/index.html docs + (cd docs && git commit -a -m 'new') + .ci/create-changes-html.sh $(cd docs && git rev-parse HEAD^) docs + (cd docs && rm -rf .git) + mv CHANGES.html docs # We also need to replace the symlinks because netlify is not following them - mkdir -p ./docs - mv CHANGES.html ./docs - cp -r -L $DOC_DIR ./docs - cp $DOC_DIR/../index.html ./docs + # CHECK IF STILL NEEDED + #cp -r -L $DOC_DIR ./docs # Zip everything for increased performance zip -r docs.zip docs @@ -157,7 +178,7 @@ jobs: id: buildlivedoc if: (success() || failure()) && steps.copy.outcome == 'success' && github.repository == 'sagemath/sage' && github.ref == 'refs/heads/develop' run: | - set -ex + export MAKE="make -j5 --output-sync=recurse" SAGE_NUM_THREADS=5 export PATH="build/bin:$PATH" eval $(sage-print-system-package-command auto update) eval $(sage-print-system-package-command auto --yes --no-install-recommends install zip) @@ -167,21 +188,18 @@ jobs: export SAGE_JUPYTER_SERVER=binder:sagemath/sage-binder-env/dev make doc-clean doc-uninstall ./config.status && make sagemath_doc_html-no-deps sagemath_doc_pdf-no-deps - working-directory: ./worktree-image - env: - MAKE: make -j4 --output-sync=recurse - SAGE_NUM_THREADS: 4 + shell: sh .ci/docker-exec-script.sh BUILD ./worktree-image {0} - name: Copy live doc id: copylivedoc if: (success() || failure()) && steps.buildlivedoc.outcome == 'success' run: | - set -ex mkdir -p ./livedoc cp -r -L /sage/local/share/doc/sage/html ./livedoc cp -r -L /sage/local/share/doc/sage/pdf ./livedoc cp /sage/local/share/doc/sage/index.html ./livedoc zip -r livedoc.zip livedoc + shell: sh .ci/docker-exec-script.sh BUILD . {0} - name: Upload live doc if: (success() || failure()) && steps.copylivedoc.outcome == 'success' diff --git a/.github/workflows/pyright.yml b/.github/workflows/pyright.yml new file mode 100644 index 00000000000..cafe75db728 --- /dev/null +++ b/.github/workflows/pyright.yml @@ -0,0 +1,84 @@ +name: Static check with Pyright + +on: + pull_request: + merge_group: + push: + branches: + - master + - develop + workflow_dispatch: + # Allow to run manually + +concurrency: + # Cancel previous runs of this workflow for the same branch + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + pyright: + runs-on: ubuntu-latest + container: ghcr.io/sagemath/sage/sage-ubuntu-jammy-standard-with-targets:dev + steps: + - name: Checkout + id: checkout + uses: actions/checkout@v4 + + - name: Update system packages + id: prepare + run: | + export PATH="build/bin:$PATH" + eval $(sage-print-system-package-command auto update) + eval $(sage-print-system-package-command auto --spkg --yes --no-install-recommends install git) + + - name: Install GH CLI + uses: dev-hanz-ops/install-gh-cli-action@v0.1.0 + with: + gh-cli-version: 2.32.0 + + - name: Merge CI fixes from sagemath/sage + run: | + git config --global --add safe.directory "$GITHUB_WORKSPACE" + .ci/merge-fixes.sh + + - name: Add prebuilt tree as a worktree + id: worktree + run: | + set -ex + .ci/retrofit-worktree.sh worktree-image /sage + + - name: Incremental build (sagelib deps) + id: incremental + run: | + # Now re-bootstrap and build. The build is incremental because we were careful with the timestamps. + # pyright does not need a built sagelib; it only needs + # the libraries from which sagelib imports. + ./bootstrap && make sagelib-build-deps + working-directory: ./worktree-image + env: + MAKE: make -j2 --output-sync=recurse + SAGE_NUM_THREADS: 2 + + - name: Static code check with pyright + uses: jakebailey/pyright-action@v1 + with: + version: 1.1.332 + # Many warnings issued by pyright are not yet helpful because there is not yet enough type information. + no-comments: true + working-directory: ./worktree-image + env: + # To avoid out of memory errors + NODE_OPTIONS: --max-old-space-size=8192 + + - name: Static code check with pyright (annotated) + if: (success() || failure()) && steps.incremental.outcome == 'success' + uses: jakebailey/pyright-action@v1 + with: + version: 1.1.332 + # Issue errors + no-comments: false + level: error + working-directory: ./worktree-image + env: + # To avoid out of memory errors + NODE_OPTIONS: --max-old-space-size=8192 diff --git a/Makefile b/Makefile index 4dadbf823b8..799358d887b 100644 --- a/Makefile +++ b/Makefile @@ -87,6 +87,14 @@ download: dist: build/make/Makefile ./sage --sdist +ci-build-with-fallback: + $(MAKE) build && $(MAKE) SAGE_CHECK=no pypi-wheels; \ + if [ $$? != 0 ]; then \ + echo "Incremental build failed, falling back"; \ + $(MAKE) doc-clean doc-uninstall sagelib-clean; \ + $(MAKE) build && $(MAKE) SAGE_CHECK=no pypi-wheels; \ + fi + ############################################################################### # Cleaning up ############################################################################### diff --git a/build/bin/write-dockerfile.sh b/build/bin/write-dockerfile.sh index 3c62d6082e4..33518bdd4a6 100755 --- a/build/bin/write-dockerfile.sh +++ b/build/bin/write-dockerfile.sh @@ -33,7 +33,7 @@ echo "# to simplify writing scripts that customize this file" ADD="ADD $__CHOWN" RUN=RUN cat <