diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0eb616c24d..f3b89a4e01 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -21,144 +21,146 @@ env: RELEASE_TAG: latest jobs: - build: - name: "Build Firedrake" - # Run on our self-hosted machines - runs-on: [self-hosted, Linux] - container: - image: firedrakeproject/firedrake-env:latest - strategy: - # Don't immediately kill real if complex fails and vice versa. - fail-fast: false - matrix: - include: - - scalar-type: real - complex: "" - petsc_arch: default - - scalar-type: complex - complex: --complex - petsc_arch: complex - env: - # PETSC_DIR and MPICH_DIR are set inside the docker image - FIREDRAKE_CI_TESTS: 1 - PYOP2_CI_TESTS: 1 - PETSC_ARCH: ${{ matrix.petsc_arch }} - OMP_NUM_THREADS: 1 - OPENBLAS_NUM_THREADS: 1 - COMPLEX: ${{ matrix.complex }} - RDMAV_FORK_SAFE: 1 - steps: - - uses: actions/checkout@v4 - - name: Cleanup - if: ${{ always() }} - run: | - cd .. - rm -rf firedrake_venv - - name: Build Firedrake - run: | - cd .. - # Linting should ignore unquoted shell variable $COMPLEX - # shellcheck disable=SC2086 - ./firedrake/scripts/firedrake-install \ - $COMPLEX \ - --honour-petsc-dir \ - --mpicc="$MPICH_DIR"/mpicc \ - --mpicxx="$MPICH_DIR"/mpicxx \ - --mpif90="$MPICH_DIR"/mpif90 \ - --mpiexec="$MPICH_DIR"/mpiexec \ - --mpihome="$MPICH_DIR"/.. \ - --venv-name firedrake_venv \ - --no-package-manager \ - --disable-ssh \ - --documentation-dependencies \ - --torch \ - --jax \ - --netgen \ - --slepc \ - --install thetis \ - --install gusto \ - --install icepack \ - --install irksome \ - --install femlium \ - --install fascd \ - --install defcon \ - --install gadopt \ - --install asQ \ - || (cat firedrake-install.log && /bin/false) - - name: Install test dependencies - run: | - . ../firedrake_venv/bin/activate - python "$(which firedrake-clean)" - python -m pip install \ - pytest-xdist pytest-timeout ipympl - python -m pip list - - name: Test Firedrake - run: | - . ../firedrake_venv/bin/activate - echo OMP_NUM_THREADS is "$OMP_NUM_THREADS" - echo OPENBLAS_NUM_THREADS is "$OPENBLAS_NUM_THREADS" - python -m pytest -v tests/firedrake/test_0init.py - python -m pytest \ - --durations=200 \ - --timeout=1800 \ - --timeout-method=thread \ - -o faulthandler_timeout=1860 \ - -n 12 --dist worksteal \ - --junit-xml=firedrake.xml \ - -sv tests/firedrake - timeout-minutes: 120 - - name: Publish Test Report - uses: mikepenz/action-junit-report@v5.0.0-a02 - if: ${{ always() && ( github.ref != 'refs/heads/master') }} - with: - report_paths: '/__w/firedrake/firedrake/firedrake.xml' - comment: true - check_name: "Firedrake ${{ matrix.scalar-type }}" - updateComment: true - flaky_summary: true - - name: Test pyadjoint - if: ${{ matrix.scalar-type == 'real' }} - run: | - . ../firedrake_venv/bin/activate - cd ../firedrake_venv/src/pyadjoint - python -m pytest \ - --durations=200 \ - --timeout=600 \ - --timeout-method=thread \ - -o faulthandler_timeout=660 \ - -n 12 --dist worksteal \ - -sv tests/firedrake_adjoint - timeout-minutes: 30 - - name: Cleanup - # Belt and braces: clean up before and after the run. - if: ${{ always() }} - run: | - cd .. - rm -rf firedrake_venv - docker_tag: - name: "Set the Docker release tag" - runs-on: [self-hosted, Linux] - if: ${{ github.ref == 'refs/heads/master' }} - steps: - - name: Set release tag - # Set a release tag if triggered by monthly CRON job - if: github.event.schedule == '0 0 1 * *' - run: | - DATE_TAG="$(date +%Y-%m)" - echo "RELEASE_TAG=$DATE_TAG" >> "$GITHUB_ENV" - - name: Print release tag being used - run: | - echo The release tag is "$RELEASE_TAG" - outputs: - tag: ${{ env.RELEASE_TAG }} + # build: + # name: "Build Firedrake" + # # Run on our self-hosted machines + # runs-on: [self-hosted, Linux] + # container: + # image: firedrakeproject/firedrake-env:latest + # strategy: + # # Don't immediately kill real if complex fails and vice versa. + # fail-fast: false + # matrix: + # include: + # - scalar-type: real + # complex: "" + # petsc_arch: default + # - scalar-type: complex + # complex: --complex + # petsc_arch: complex + # env: + # # PETSC_DIR and MPICH_DIR are set inside the docker image + # FIREDRAKE_CI_TESTS: 1 + # PYOP2_CI_TESTS: 1 + # PETSC_ARCH: ${{ matrix.petsc_arch }} + # OMP_NUM_THREADS: 1 + # OPENBLAS_NUM_THREADS: 1 + # COMPLEX: ${{ matrix.complex }} + # RDMAV_FORK_SAFE: 1 + # steps: + # - uses: actions/checkout@v4 + # - name: Cleanup + # if: ${{ always() }} + # run: | + # cd .. + # rm -rf firedrake_venv + # - name: Build Firedrake + # run: | + # cd .. + # # Linting should ignore unquoted shell variable $COMPLEX + # # shellcheck disable=SC2086 + # ./firedrake/scripts/firedrake-install \ + # $COMPLEX \ + # --honour-petsc-dir \ + # --mpicc="$MPICH_DIR"/mpicc \ + # --mpicxx="$MPICH_DIR"/mpicxx \ + # --mpif90="$MPICH_DIR"/mpif90 \ + # --mpiexec="$MPICH_DIR"/mpiexec \ + # --mpihome="$MPICH_DIR"/.. \ + # --venv-name firedrake_venv \ + # --no-package-manager \ + # --disable-ssh \ + # --documentation-dependencies \ + # --torch \ + # --jax \ + # --netgen \ + # --slepc \ + # --install thetis \ + # --install gusto \ + # --install icepack \ + # --install irksome \ + # --install femlium \ + # --install fascd \ + # --install defcon \ + # --install gadopt \ + # --install asQ \ + # || (cat firedrake-install.log && /bin/false) + # - name: Install test dependencies + # run: | + # . ../firedrake_venv/bin/activate + # python "$(which firedrake-clean)" + # python -m pip install \ + # pytest-xdist pytest-timeout ipympl + # python -m pip list + # - name: Test Firedrake + # run: | + # . ../firedrake_venv/bin/activate + # echo OMP_NUM_THREADS is "$OMP_NUM_THREADS" + # echo OPENBLAS_NUM_THREADS is "$OPENBLAS_NUM_THREADS" + # python -m pytest -v tests/firedrake/test_0init.py + # python -m pytest \ + # --durations=200 \ + # --timeout=1800 \ + # --timeout-method=thread \ + # -o faulthandler_timeout=1860 \ + # -n 12 --dist worksteal \ + # --junit-xml=firedrake.xml \ + # -sv tests/firedrake + # timeout-minutes: 120 + # - name: Publish Test Report + # uses: mikepenz/action-junit-report@v5.0.0-a02 + # if: ${{ always() && ( github.ref != 'refs/heads/master') }} + # with: + # report_paths: '/__w/firedrake/firedrake/firedrake.xml' + # comment: true + # check_name: "Firedrake ${{ matrix.scalar-type }}" + # updateComment: true + # flaky_summary: true + # - name: Test pyadjoint + # if: ${{ matrix.scalar-type == 'real' }} + # run: | + # . ../firedrake_venv/bin/activate + # cd ../firedrake_venv/src/pyadjoint + # python -m pytest \ + # --durations=200 \ + # --timeout=600 \ + # --timeout-method=thread \ + # -o faulthandler_timeout=660 \ + # -n 12 --dist worksteal \ + # -sv tests/firedrake_adjoint + # timeout-minutes: 30 + # - name: Cleanup + # # Belt and braces: clean up before and after the run. + # if: ${{ always() }} + # run: | + # cd .. + # rm -rf firedrake_venv + # + # docker_tag: + # name: "Set the Docker release tag" + # runs-on: [self-hosted, Linux] + # if: ${{ github.ref == 'refs/heads/master' }} + # steps: + # - name: Set release tag + # # Set a release tag if triggered by monthly CRON job + # if: github.event.schedule == '0 0 1 * *' + # run: | + # DATE_TAG="$(date +%Y-%m)" + # echo "RELEASE_TAG=$DATE_TAG" >> "$GITHUB_ENV" + # - name: Print release tag being used + # run: | + # echo The release tag is "$RELEASE_TAG" + # outputs: + # tag: ${{ env.RELEASE_TAG }} + docker: name: "Build Docker containers" # Only run on master, but always generate firedrake-env image, # even if build fails (see docker.yml) - if: ${{ (github.ref == 'refs/heads/master') && always() }} - needs: [build, docker_tag] + # if: ${{ (github.ref == 'refs/heads/master') && always() }} + if: ${{ always() }} uses: ./.github/workflows/docker.yml with: - tag: ${{ needs.docker_tag.outputs.tag }} - status: ${{ needs.build.result }} + tag: testing + status: success secrets: inherit diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 6e6bbcdb28..e5c0a07b7a 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -32,7 +32,7 @@ jobs: docker_vanilla: needs: docker_env # Only run if "Build Firedrake" succeeds - if: ${{ inputs.status == 'success' }} + # if: ${{ inputs.status == 'success' }} uses: ./.github/workflows/docker_reuse.yml with: target: firedrake-vanilla @@ -63,7 +63,7 @@ jobs: docker_complex: needs: docker_env # Only run if "Build Firedrake" succeeds - if: ${{ inputs.status == 'success' }} + # if: ${{ inputs.status == 'success' }} uses: ./.github/workflows/docker_reuse.yml with: target: firedrake-complex diff --git a/.github/workflows/docker_reuse.yml b/.github/workflows/docker_reuse.yml index 59e61e8b9a..9e20f76fbc 100644 --- a/.github/workflows/docker_reuse.yml +++ b/.github/workflows/docker_reuse.yml @@ -24,24 +24,93 @@ on: required: true jobs: - docker_workflow: + docker_build: name: "Build the ${{ inputs.target }} container" - runs-on: [self-hosted, Linux] + strategy: + fail-fast: false + # see https://docs.docker.com/build/ci/github-actions/multi-platform/ + matrix: + os: [Linux, macOS] + include: + - os: Linux + platform: linux/amd64 + - os: macOS + platform: linux/arm64 + runs-on: [self-hosted, "${{ matrix.os }}"] steps: - name: Check out the repo uses: actions/checkout@v4 + + - name: Add homebrew to PATH + if: ${{ matrix.os == 'macOS' }} + # https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/workflow-commands-for-github-actions#adding-a-system-path + run: echo "/opt/homebrew/bin" >> "$GITHUB_PATH" + + - name: Prepare environment + run: | + platform=${{ matrix.platform }} + echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV + - name: Log in to Docker Hub uses: docker/login-action@v3 with: username: ${{ secrets.DOCKERHUB_USER }} password: ${{ secrets.DOCKERHUB_TOKEN }} + - name: Set up Docker Buildx - id: buildx uses: docker/setup-buildx-action@v3 + - name: Build and push ${{ inputs.target }} + id: build uses: docker/build-push-action@v6 with: - push: true - no-cache: true + platforms: ${{ matrix.platform }} file: ${{ inputs.dockerfile }} - tags: firedrakeproject/${{ inputs.target }}:${{ inputs.tag }} + outputs: type=image,name=firedrakeproject/${{ inputs.target }},push-by-digest=true,name-canonical=true,push=true + + - name: Export digest + run: | + mkdir -p ${{ runner.temp }}/digests + digest="${{ steps.build.outputs.imageid }}" + touch "${{ runner.temp }}/digests/${digest#sha256:}" + + - name: Upload digest + uses: actions/upload-artifact@v4 + with: + name: digests-${{ env.PLATFORM_PAIR }} + path: ${{ runner.temp }}/digests/* + if-no-files-found: error + retention-days: 1 + + docker_merge: + runs-on: [self-hosted, Linux] + container: + image: ubuntu:latest + needs: + - docker_build + steps: + - name: Download digests + uses: actions/download-artifact@v4 + with: + path: ${{ runner.temp }}/digests + pattern: digests-* + merge-multiple: true + + - name: Log in to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USER }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Create manifest list and push + working-directory: ${{ runner.temp }}/digests + run: | + docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \ + $(printf 'firedrakeproject/${{ inputs.target }}@sha256:%s ' *) + + - name: Inspect image + run: | + docker buildx imagetools inspect firedrakeproject/${{ inputs.target }}:${{ inputs.tag }} diff --git a/docker/Dockerfile.complex b/docker/Dockerfile.complex index 6aa73f280c..98bac1f60b 100644 --- a/docker/Dockerfile.complex +++ b/docker/Dockerfile.complex @@ -1,6 +1,6 @@ # DockerFile for Firedrake in complex mode with a full set of capabilities. -FROM firedrakeproject/firedrake-env:latest +FROM firedrakeproject/firedrake-env:testing USER firedrake WORKDIR /home/firedrake diff --git a/docker/Dockerfile.docdeps b/docker/Dockerfile.docdeps index 8339962262..167c24fb3c 100644 --- a/docker/Dockerfile.docdeps +++ b/docker/Dockerfile.docdeps @@ -1,6 +1,6 @@ # DockerFile for Firedrake with a full set of capabilities and applications installed. -FROM firedrakeproject/firedrake-vanilla:latest +FROM firedrakeproject/firedrake-vanilla:testing USER root WORKDIR /home/firedrake diff --git a/docker/Dockerfile.firedrake b/docker/Dockerfile.firedrake index 490a16e59f..e7c5360059 100644 --- a/docker/Dockerfile.firedrake +++ b/docker/Dockerfile.firedrake @@ -1,6 +1,6 @@ # DockerFile for Firedrake with a full set of capabilities and applications installed. -FROM firedrakeproject/firedrake-vanilla:latest +FROM firedrakeproject/firedrake-vanilla:testing USER firedrake WORKDIR /home/firedrake diff --git a/docker/Dockerfile.jupyter b/docker/Dockerfile.jupyter index 5f36e75672..66295480d3 100644 --- a/docker/Dockerfile.jupyter +++ b/docker/Dockerfile.jupyter @@ -1,6 +1,6 @@ # DockerFile for a firedrake + jupyter container -FROM firedrakeproject/firedrake:latest +FROM firedrakeproject/firedrake:testing USER firedrake WORKDIR /home/firedrake diff --git a/docker/Dockerfile.vanilla b/docker/Dockerfile.vanilla index eee3ef53e3..90dbaa88b4 100644 --- a/docker/Dockerfile.vanilla +++ b/docker/Dockerfile.vanilla @@ -1,6 +1,6 @@ # DockerFile for a plain Firedrake suitable for testing Firedrake components and applications -FROM firedrakeproject/firedrake-env:latest +FROM firedrakeproject/firedrake-env:testing USER firedrake WORKDIR /home/firedrake