diff --git a/.devcontainer/devcontainer-lock.json b/.devcontainer/devcontainer-lock.json new file mode 100644 index 0000000..4265c48 --- /dev/null +++ b/.devcontainer/devcontainer-lock.json @@ -0,0 +1,29 @@ +{ + "features": { + "ghcr.io/devcontainers/features/common-utils:2": { + "version": "2.5.3", + "resolved": "ghcr.io/devcontainers/features/common-utils@sha256:3cf7ca93154faf9bdb128f3009cf1d1a91750ec97cc52082cf5d4edef5451f85", + "integrity": "sha256:3cf7ca93154faf9bdb128f3009cf1d1a91750ec97cc52082cf5d4edef5451f85" + }, + "ghcr.io/devcontainers/features/docker-in-docker:2": { + "version": "2.12.2", + "resolved": "ghcr.io/devcontainers/features/docker-in-docker@sha256:842d2ed40827dc91b95ef727771e170b0e52272404f00dba063cee94eafac4bb", + "integrity": "sha256:842d2ed40827dc91b95ef727771e170b0e52272404f00dba063cee94eafac4bb" + }, + "ghcr.io/devcontainers/features/github-cli:1": { + "version": "1.0.14", + "resolved": "ghcr.io/devcontainers/features/github-cli@sha256:ca677566507c4118e4368cd76a4800807e911e5e350cc3525fb67ebc9132dfa1", + "integrity": "sha256:ca677566507c4118e4368cd76a4800807e911e5e350cc3525fb67ebc9132dfa1" + }, + "ghcr.io/edouard-lopez/devcontainer-features/bats:0": { + "version": "0.0.1", + "resolved": "ghcr.io/edouard-lopez/devcontainer-features/bats@sha256:8acd020fc45ebacdd399a29977c4d22cad56b5c95c43cdf2ae061f0e755aea7a", + "integrity": "sha256:8acd020fc45ebacdd399a29977c4d22cad56b5c95c43cdf2ae061f0e755aea7a" + }, + "ghcr.io/joshuanianji/devcontainer-features/github-cli-persistence:1": { + "version": "1.0.3", + "resolved": "ghcr.io/joshuanianji/devcontainer-features/github-cli-persistence@sha256:757843d75915f140ec22bc16ccb5e657a910d9b1d1f445d15350a78e584d130f", + "integrity": "sha256:757843d75915f140ec22bc16ccb5e657a910d9b1d1f445d15350a78e584d130f" + } + } +} \ No newline at end of file diff --git a/.devcontainer.json b/.devcontainer/devcontainer.json similarity index 100% rename from .devcontainer.json rename to .devcontainer/devcontainer.json diff --git a/.github/workflows/version-base-build-test.yml b/.github/workflows/version-base-build-test.yml index 2fc997b..5020b72 100644 --- a/.github/workflows/version-base-build-test.yml +++ b/.github/workflows/version-base-build-test.yml @@ -104,73 +104,3 @@ jobs: run: | docker run ${{ env.TAG }} /bin/bash -c "make test" - # "consumer" images just "consume" the base image and do nothing else - # basic tests for these - consumer-build-test: - name: Build Consumer - runs-on: ubuntu-latest - needs: [base-test-1, base-test-2] - strategy: - matrix: - dockerfile: [debian, ubuntu] - # use a local registry - services: - registry: - image: registry:2 - ports: - - 5000:5000 - env: - # use local registry so we can reference the base image in another dockerfile - # https://github.com/docker/build-push-action/issues/738 - BASE_TAG: localhost:5000/base-${{ inputs.idris-version }}:latest - TAG: idris-consumer-${{ matrix.dockerfile }}-${{ inputs.idris-version }}:test - steps: - - name: Checkout Repo - uses: actions/checkout@v4 - - - name: Setup Buildx (no ARM) - uses: docker/setup-buildx-action@v3 - with: - # since we're using a local registry - driver-opts: network=host - - - name: Build Base Image - uses: ./.github/actions/build-base-img - with: - idris-version: ${{ inputs.idris-version }} - push: true - tags: ${{ env.BASE_TAG }} - - - name: Run `docker image ls` - run: docker image ls - - - name: Build ${{ matrix.dockerfile}} - uses: docker/build-push-action@v6 - with: - context: . - file: ${{ matrix.dockerfile }}.Dockerfile - build-args: | - IDRIS_VERSION=${{ inputs.idris-version }} - BASE_IMG=${{ env.BASE_TAG }} - tags: ${{ env.TAG }} - load: true - - - name: Setup Bats and Bats libs - uses: bats-core/bats-action@3.0.0 - with: - bats-version: 1.10.0 - support-version: 0.3.0 - support-path: ${{ github.workspace }}/.bats/bats-support - assert-version: 2.1.0 - assert-path: ${{ github.workspace }}/.bats/bats-assert - file-install: false - detik-install: false - - # LIB_PATH is a env var I use in the setup() function of my bats tests - # it points to the folder containing bats-assert and bats-support - - name: Run Test - env: - LIB_PATH: ${{ github.workspace }}/.bats - DOCKER_IMAGE: ${{ env.TAG }} - IDRIS_VERSION: ${{ inputs.idris-version }} - run: bats tests/consumer-idris.bats diff --git a/.github/workflows/version-base-deploy.yml b/.github/workflows/version-base-deploy.yml index 78e85fd..77826a4 100644 --- a/.github/workflows/version-base-deploy.yml +++ b/.github/workflows/version-base-deploy.yml @@ -54,46 +54,3 @@ jobs: labels: ${{ steps.create-meta.outputs.labels }} platforms: linux/amd64,linux/arm64 - deploy-consumers: - name: Deploy Consumer - runs-on: ubuntu-latest - strategy: - matrix: - dockerfile: [ubuntu, debian] - needs: deploy-base - steps: - - name: Checkout Repo - uses: actions/checkout@v4 - - # instead of native builds, we'll just use QEMU for consumers - # consumer builds don't really do much intensive stuff anyway - - name: Set up QEMU - uses: docker/setup-qemu-action@v3 - - - name: Setup Buildx - uses: docker/setup-buildx-action@v3 - - - name: Login to GHCR - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Docker meta - id: create-meta - uses: docker/metadata-action@v5 - with: - images: ${{ github.repository }}/${{ matrix.dockerfile }} - - - name: Build ${{ matrix.dockerfile}} - uses: docker/build-push-action@v6 - with: - context: . - platforms: linux/amd64,linux/arm64 - file: ${{ matrix.dockerfile }}.Dockerfile - build-args: | - IDRIS_VERSION=${{ inputs.idris-version }} - BASE_IMG=ghcr.io/${{ github.repository }}/base:${{ inputs.idris-version }} - tags: ghcr.io/${{ github.repository }}/${{ matrix.dockerfile }}:${{ inputs.idris-version }} - push: true diff --git a/README.md b/README.md index 32301bb..a0be559 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,7 @@ Idris Versions: `v0.5.1`, `v0.6.0`, `v0.7.0`, `latest` (Up to date with [Idris2/ - [Running Locally](#running-locally) - [Build Latest](#build-latest) - [Build From a Tagged Release/SHA commit](#build-from-a-tagged-releasesha-commit) + - [Running tests](#running-tests) - [Credit](#credit) ## Motivation @@ -28,10 +29,8 @@ Installing Idris2 is [quite time consuming](https://idris2.readthedocs.io/en/lat ## Images -* [idris-2-docker/base](https://github.com/joshuanianji/idris-2-docker/pkgs/container/idris-2-docker%2Fbase) - Base image with Idris2 installed from source. +* [idris-2-docker/base](https://github.com/joshuanianji/idris-2-docker/pkgs/container/idris-2-docker%2Fbase) - Base image with Idris2 installed from source (debian-based) * [idris-2-docker/devcontainer](https://github.com/joshuanianji/idris-2-docker/pkgs/container/idris-2-docker%2Fdevcontainer) - Includes [Idris2 LSP](https://github.com/idris-community/idris2-lsp) -* [idris-2-docker/ubuntu](https://github.com/joshuanianji/idris-2-docker/pkgs/container/idris-2-docker%2Fubuntu) - Ubuntu 20.04 -* [idris-2-docker/debian](https://github.com/joshuanianji/idris-2-docker/pkgs/container/idris-2-docker%2Fdebian) - Debian bullseye ## Example Project @@ -86,17 +85,41 @@ To run the images locally, I recommend opening the workspace in the Devcontainer This is the default behaviour when running the script. ```bash +# this is the only module we need, so i chose not to create a requirements.txt file +pip install -U requests + # builds base from latest commit on the Idris repo. Tag is base-latest python scripts/build-image.py --image base -python scripts/build-image.py --image devcontainer --tag devcontainer-latest-test +python scripts/build-image.py --image ubuntu +python scripts/build-image.py --image devcontainer --tag devcontainer-latest ``` ### Build From a Tagged Release/SHA commit ```bash -python scripts/build-image.py --image base --version v0.6.0 +python scripts/build-image.py --image base --version v0.7.0 python scripts/build-image.py --image base --sha 58e5d156621cfdd4c54df26abf7ac9620cfebdd8 -python scripts/build-image.py --image devcontainer --version v0.6.0 +python scripts/build-image.py --image devcontainer --version v0.7.0 +``` + +### Running tests + +We have some tests to ensure the docker images are working as expected, using [bats](https://github.com/bats-core/bats-core). The bats CLI tool should already be installed in the devcontainer. + +You'll need to install the `bats-support` and `bats-assert` libraries. I found the easiest way to do this was to clone via git: + +```bash +git clone https://github.com/bats-core/bats-support test/test_helper/bats-support +git clone https://github.com/bats-core/bats-assert test/test_helper/bats-assert +``` + +To run a test on a specific image, set the required variables, and run the bats command. The following is an example for the base image + +```bash +export LIB_PATH=$(pwd)/tests/test_helper +export DOCKER_IMAGE= +export IDRIS_VERSION=latest +bats tests/base.bats ``` ## Credit diff --git a/base-sha.Dockerfile b/base-sha.Dockerfile index 91936ac..2219297 100644 --- a/base-sha.Dockerfile +++ b/base-sha.Dockerfile @@ -3,7 +3,7 @@ # separating this into a separate file so we can get better caching # there is a lot of redundancy between this and base.Dockerfile, but I don't think it's worth it to try to abstract it out -FROM debian:bullseye as scheme-builder +FROM debian:bullseye AS scheme-builder WORKDIR /root @@ -22,12 +22,12 @@ RUN which scheme # this makes it a bit easier for us to move the csv folder to other build steps, since it is necessary for scheme to run RUN mkdir scheme-lib && cp -r /usr/lib/csv* /root/scheme-lib -FROM debian:bullseye as idris-builder +FROM debian:bullseye AS idris-builder RUN apt-get update && \ apt-get install -y git make gcc libgmp-dev curl -ENV DEBIAN_FRONTEND noninteractive +ENV DEBIAN_FRONTEND=noninteractive # SHA of the latest commit on the idris-lang/idris2 repo ARG IDRIS_SHA diff --git a/base.Dockerfile b/base.Dockerfile index aa3a2c8..15ed44c 100644 --- a/base.Dockerfile +++ b/base.Dockerfile @@ -1,7 +1,7 @@ # Expects IDRIS_VERSION to NOT be `latest` -# uBut a tag of the form `v0.7.0` +# But a tag of the form `v0.7.0` -FROM debian:bullseye as scheme-builder +FROM debian:bullseye AS scheme-builder WORKDIR /root @@ -20,12 +20,12 @@ RUN which scheme # this makes it a bit easier for us to move the csv folder to other build steps, since it is necessary for scheme to run RUN mkdir scheme-lib && cp -r /usr/lib/csv* /root/scheme-lib -FROM debian:bullseye as idris-builder +FROM debian:bullseye AS idris-builder RUN apt-get update && \ apt-get install -y git make gcc libgmp-dev curl -ENV DEBIAN_FRONTEND noninteractive +ENV DEBIAN_FRONTEND=noninteractive ARG IDRIS_VERSION COPY --from=scheme-builder /usr/bin/scheme /usr/bin/scheme diff --git a/debian.Dockerfile b/debian.Dockerfile deleted file mode 100644 index cc58f84..0000000 --- a/debian.Dockerfile +++ /dev/null @@ -1,17 +0,0 @@ -ARG IDRIS_VERSION=latest -ARG BASE_IMG=ghcr.io/joshuanianji/idris-2-docker/base:${IDRIS_VERSION} - -FROM $BASE_IMG as base - -FROM debian:bullseye - -# add idris2 and scheme from builder -COPY --from=base /root/.idris2 /root/.idris2 -COPY --from=base /usr/bin/scheme /usr/bin/scheme -# copy csv* to /usr/lib -COPY --from=base /root/scheme-lib/ /usr/lib/ - -# add idris2 to path -ENV PATH="/root/.idris2/bin:${PATH}" - -ENTRYPOINT ["idris2"] diff --git a/devcontainer-sha.Dockerfile b/devcontainer-sha.Dockerfile index 8e6d9ae..b6c46b4 100644 --- a/devcontainer-sha.Dockerfile +++ b/devcontainer-sha.Dockerfile @@ -7,7 +7,7 @@ ARG BASE_IMG=ghcr.io/joshuanianji/idris-2-docker/base:${IDRIS_VERSION} # ===== # Idris Builder # Rebuild with correct prefix. Somehow, building from scratch with a different prefix fails -FROM $BASE_IMG as idris-builder +FROM $BASE_IMG AS idris-builder ARG IDRIS_VERSION ARG IDRIS_SHA diff --git a/devcontainer.Dockerfile b/devcontainer.Dockerfile index 79a5188..f90f9c4 100644 --- a/devcontainer.Dockerfile +++ b/devcontainer.Dockerfile @@ -6,7 +6,7 @@ ARG BASE_IMG=ghcr.io/joshuanianji/idris-2-docker/base:${IDRIS_VERSION} # ===== # Idris Builder # Rebuild with correct prefix. Somehow, building from scratch with a different prefix fails -FROM $BASE_IMG as idris-builder +FROM $BASE_IMG AS idris-builder ARG IDRIS_VERSION WORKDIR /build diff --git a/scripts/build-image.py b/scripts/build-image.py index 7018cc8..1bca5cd 100644 --- a/scripts/build-image.py +++ b/scripts/build-image.py @@ -8,8 +8,10 @@ def get_latest_sha(): - idris_latest_sha = requests.get('https://api.github.com/repos/idris-lang/Idris2/commits').json()[0]['sha'] - lsp_latest_sha = requests.get('https://api.github.com/repos/idris-community/idris2-lsp/commits').json()[0]['sha'] + idris_latest_sha = requests.get( + 'https://api.github.com/repos/idris-lang/Idris2/commits').json()[0]['sha'] + lsp_latest_sha = requests.get( + 'https://api.github.com/repos/idris-community/idris2-lsp/commits').json()[0]['sha'] return { 'idris': idris_latest_sha, 'lsp': lsp_latest_sha @@ -40,34 +42,34 @@ def build_image_sha(image: str, sha_info: dict, idris_base_version: str, tag: st # for sha-specific devcontainer images, we also need to pass in the sha from the idris2 github repo # by default, this is the latest idris2 sha. idris_sha = sha_info['idris'] if idris_base_version == 'latest' else idris_base_version - subprocess.run([ 'docker', 'build', '-t', tag, '-f', dockerfile, - '--build-arg', f'IDRIS_SHA={idris_sha}', - '--build-arg', f'IDRIS_LSP_SHA={sha_info["lsp"]}', + subprocess.run(['docker', 'build', '-t', tag, '-f', dockerfile, + '--build-arg', f'IDRIS_SHA={idris_sha}', + '--build-arg', f'IDRIS_LSP_SHA={sha_info["lsp"]}', '--build-arg', f'IDRIS_VERSION={idris_base_version}', - '.' ]) + '.']) else: - subprocess.run([ 'docker', 'build', '-t', tag, '-f', dockerfile, - '--build-arg', f'IDRIS_SHA={sha_info["idris"]}', '.' ]) + subprocess.run(['docker', 'build', '-t', tag, '-f', dockerfile, + '--build-arg', f'IDRIS_SHA={sha_info["idris"]}', '.']) print(f'Image built with tag {tag}') if __name__ == '__main__': parser = argparse.ArgumentParser(description='Builds a docker image') parser.add_argument( - '--image', - help='The image to build. One of (base | debian | ubuntu | devcontainer). Defaults to base.', + '--image', + help='The image to build. One of (base | devcontainer). Defaults to base.', default='base') group = parser.add_mutually_exclusive_group() group.add_argument( - '--version', + '--version', help='Idris version to use. Defaults to `latest`, and of the form `v0.6.0`', default='latest') group.add_argument( - '--sha', - help='Idris/Idris LSP SHA to use. Should not be used with `--version`. SHAs also cannot be used with base or debian images.', + '--sha', + help='Idris/Idris LSP SHA to use. Should not be used with `--version`. SHAs cannot be used with base images.', default=None) parser.add_argument( - '--tag', + '--tag', help='Tag to use for the image. Defaults to `{image}-{version}` or `{image}-{tag}.', default=None) parser.add_argument( @@ -77,10 +79,10 @@ def build_image_sha(image: str, sha_info: dict, idris_base_version: str, tag: st ) args = parser.parse_args() - if args.image not in ['base', 'debian', 'ubuntu', 'devcontainer']: - print('Invalid image. Must be one of (base | debian | ubuntu | devcontainer).') + if args.image not in ['base', 'devcontainer']: + print('Invalid image. Must be one of (base | devcontainer).') exit(1) - + if args.version and args.version != 'latest': # Build versioned image. dockerfile = f'{args.image}.Dockerfile' @@ -90,24 +92,30 @@ def build_image_sha(image: str, sha_info: dict, idris_base_version: str, tag: st # build image if args.image == 'devcontainer': lsp_version = get_lsp_version(args.version) - subprocess.run([ 'docker', 'build', '-t', tag, '-f', dockerfile, - '--build-arg', f'IDRIS_VERSION={args.version}', - '--build-arg', f'IDRIS_LSP_VERSION={lsp_version}', - '.' ]) + subprocess.run(['docker', 'build', '-t', tag, '-f', dockerfile, + '--build-arg', f'IDRIS_VERSION={args.version}', + '--build-arg', f'IDRIS_LSP_VERSION={lsp_version}', + '.']) else: - subprocess.run([ 'docker', 'build', '-t', tag, '-f', dockerfile, '--build-arg', f'IDRIS_VERSION={args.version}', '.' ]) + subprocess.run(['docker', 'build', '-t', tag, '-f', dockerfile, + '--build-arg', f'IDRIS_VERSION={args.version}', '.']) print(f'Image built with tag {tag}') elif args.version == 'latest': - sha_info = get_latest_sha() - tag = f'{args.image}-latest' if not args.tag else args.tag - build_image_sha(args.image, sha_info, args.idris_base_version, tag) + if args.image in ['base', 'devcontainer']: + # base and devcontainer need idris' latest SHA to build + sha_info = get_latest_sha() + tag = f'{args.image}-latest' if not args.tag else args.tag + build_image_sha(args.image, sha_info, args.idris_base_version, tag) - elif args.sha: - if args.image in ['base', 'debian']: - print('Cannot build base or debian images with a sha.') - exit(1) + else: + tag = f'{args.image}-latest' if not args.tag else args.tag + print(f'Building {args.image}.Dockerfile with tag {tag}') + subprocess.run(['docker', 'build', '-t', tag, '-f', + f'{args.image}.Dockerfile', '.']) + print(f'Image built with tag {tag}') + elif args.sha: sha_info = { 'idris': args.sha, 'lsp': args.sha @@ -117,4 +125,4 @@ def build_image_sha(image: str, sha_info: dict, idris_base_version: str, tag: st else: print('This should never happen.') - exit(1) \ No newline at end of file + exit(1) diff --git a/tests/consumer-idris.bats b/tests/consumer-idris.bats deleted file mode 100644 index e13c481..0000000 --- a/tests/consumer-idris.bats +++ /dev/null @@ -1,52 +0,0 @@ -# Consumer idris -# Tests the idris installation inside the "consumer" images - Ubuntu and Debian - -function setup() { - # Assumes the BATS_TEST_FILENAME is always one layer deep inside tests/ - # not a very good assumption! But i'm not going to be using bats much right now... - DIR="$( dirname $( dirname "$BATS_TEST_FILENAME" ) )" - - # by default, look in tests/test_helper - if [[ -z "${LIB_PATH}" ]]; then - LIB_PATH="$DIR/tests/test_helper" - fi - - load "$LIB_PATH/bats-support/load" - load "$LIB_PATH/bats-assert/load" -} - -@test "Test location of Idris binary" { - # note that the consumer images have an entrypoint of the "idris2" binary - run docker run --entrypoint /bin/bash $DOCKER_IMAGE which idris2 - assert_output --partial '/root/.idris2/bin/idris2' -} - -@test "Test idris version" { - # since we can't guess the idris version (e.g. the commit hash if we're on latest) - # we just test if it has "Idris 2, version" at the beginning - run docker run $DOCKER_IMAGE --version - assert_output --partial 'Idris 2, version' -} - -@test "Test Idris2 command output" { - # make sure it doesn't have "Module Prelude not found" - # or, any other errors - # https://github.com/joshuanianji/idris-2-docker/issues/16#issuecomment-1254561254 - - # the ENTRYPOINT is already the `idris2` command, so we just `docker run` without any other stuff - run docker run $DOCKER_IMAGE - refute_output --partial "Uncaught error:" -} - -@test "Check environment variables" { - # make sure IDRIS_SHA is set - # https://bats-core.readthedocs.io/en/stable/writing-tests.html#run-test-other-commands - # expects the cmd to return 0 - if [[ $IDRIS_VERSION != "latest" ]]; then - # For versions that are not latest, IDRIS_VERSION should be set - docker run $DOCKER_IMAGE bash -c "if [[ -z \$IDRIS_VERSION ]]; then exit 1; else exit 0; fi" - else - # For latest, IDRIS_SHA should be set - docker run $DOCKER_IMAGE bash -c "if [[ -z \$IDRIS_SHA ]]; then exit 1; else exit 0; fi" - fi -} \ No newline at end of file diff --git a/ubuntu.Dockerfile b/ubuntu.Dockerfile deleted file mode 100644 index 9615b64..0000000 --- a/ubuntu.Dockerfile +++ /dev/null @@ -1,17 +0,0 @@ -ARG IDRIS_VERSION=latest -ARG BASE_IMG=ghcr.io/joshuanianji/idris-2-docker/base:${IDRIS_VERSION} - -FROM $BASE_IMG as base - -FROM ubuntu:20.04 - -# add idris2 and scheme from builder -COPY --from=base /root/.idris2 /root/.idris2 -COPY --from=base /usr/bin/scheme /usr/bin/scheme -# copy csv* to /usr/lib -COPY --from=base /root/scheme-lib/ /usr/lib/ - -# add idris2 to path -ENV PATH="/root/.idris2/bin:${PATH}" - -ENTRYPOINT ["idris2"]