From 286996b52c5ff1df986395759978502bdf0db3bf Mon Sep 17 00:00:00 2001 From: Ashe Connor Date: Mon, 14 Jan 2019 13:28:53 +1100 Subject: [PATCH] Docker helpers --- .gitmodules | 3 + .travis.yml | 7 + docker/Dockerfile-develop | 21 +++ docker/Dockerfile-distpkg | 58 ++++++++ docker/Dockerfile-fuse3 | 36 +++++ docker/Dockerfile-integrate | 19 +++ docker/Dockerfile-vfs | 5 + docker/README.md | 136 +++++++++++++++++++ docker/VFSForGit | 1 + docker/build/BuildOutput/.gitignore | 2 + docker/build/integrate/.gitignore | 2 + docker/build/packages/.gitignore | 2 + docker/cibuild | 20 +++ docker/project.rb | 80 +++++++++++ docker/projfs | 201 ++++++++++++++++++++++++++++ docker/tests.rb | 74 ++++++++++ docker/update-readme | 18 +++ script/cibuild | 8 -- 18 files changed, 685 insertions(+), 8 deletions(-) create mode 100644 .gitmodules create mode 100644 .travis.yml create mode 100644 docker/Dockerfile-develop create mode 100644 docker/Dockerfile-distpkg create mode 100644 docker/Dockerfile-fuse3 create mode 100644 docker/Dockerfile-integrate create mode 100644 docker/Dockerfile-vfs create mode 100644 docker/README.md create mode 160000 docker/VFSForGit create mode 100644 docker/build/BuildOutput/.gitignore create mode 100644 docker/build/integrate/.gitignore create mode 100644 docker/build/packages/.gitignore create mode 100755 docker/cibuild create mode 100644 docker/project.rb create mode 100755 docker/projfs create mode 100644 docker/tests.rb create mode 100755 docker/update-readme delete mode 100755 script/cibuild diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..cc074d9 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "docker/VFSForGit"] + path = docker/VFSForGit + url = https://github.com/github/VFSForGit.git diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..506c02b --- /dev/null +++ b/.travis.yml @@ -0,0 +1,7 @@ +language: ruby + +services: +- docker + +script: +- docker/cibuild diff --git a/docker/Dockerfile-develop b/docker/Dockerfile-develop new file mode 100644 index 0000000..cf050b8 --- /dev/null +++ b/docker/Dockerfile-develop @@ -0,0 +1,21 @@ +FROM github/fuse3-linux + +LABEL org.label-schema.name="projfs-develop" +LABEL org.label-schema.description="projfs Linux libprojfs development image" +LABEL org.label-schema.vendor="GitHub" +LABEL org.label-schema.schema-version="1.0" + +WORKDIR /data/projfs + +RUN set -ex && \ + apt-get update -qq && \ + BUILD_DEPS=' \ + automake \ + build-essential \ + libtool \ + ' && \ + apt-get install -y -qq --no-install-recommends $BUILD_DEPS && \ + rm -rf /var/lib/apt/lists/* + +ARG UID +RUN useradd -d /data -M -s /bin/bash -u $UID -G 0 user diff --git a/docker/Dockerfile-distpkg b/docker/Dockerfile-distpkg new file mode 100644 index 0000000..9181d32 --- /dev/null +++ b/docker/Dockerfile-distpkg @@ -0,0 +1,58 @@ +FROM github/fuse3-linux + +LABEL org.label-schema.name="projfs-distpkg" +LABEL org.label-schema.description="projfs Linux libprojfs distribution image" +LABEL org.label-schema.vendor="GitHub" +LABEL org.label-schema.schema-version="1.0" + +ARG PROJFS_REPO=projfs-linux-private + +ENV BUILD_DEPS ' \ + automake \ + build-essential \ + dpkg-dev \ + libtool \ + pkg-config \ + ' + +RUN apt-get update -qq && \ + apt-get install -y -qq --no-install-recommends $BUILD_DEPS && \ + rm -rf /var/lib/apt/lists/* + +COPY repos/$PROJFS_REPO /tmp/$PROJFS_REPO + +WORKDIR /tmp/$PROJFS_REPO +RUN ./autogen.sh +RUN make -j "$(nproc)" distclean +RUN ./configure --enable-vfs-api +RUN make -j "$(nproc)" dist + +WORKDIR /tmp +RUN \ + PROJFS_VERSION=$(pkg-config --modversion $PROJFS_REPO/projfs.pc) && \ + PROJFS_RELEASE="libprojfs-$PROJFS_VERSION" && \ + PROJFS_DISTPKG="$PROJFS_RELEASE.tar.xz" && \ + \ + tar -xJf "$PROJFS_REPO/$PROJFS_DISTPKG" && \ + rm -rf "$PROJFS_REPO" && \ + mv "$PROJFS_RELEASE" libprojfs-release + +WORKDIR /tmp/libprojfs-release +RUN \ + DEB_HOST_MULTIARCH="$(dpkg-architecture --query DEB_HOST_MULTIARCH)" && \ + ./configure --prefix=/usr \ + --libdir="\${prefix}/lib/$DEB_HOST_MULTIARCH" \ + --libexecdir="\${prefix}/lib/$DEB_HOST_MULTIARCH" \ + --sysconfdir=/etc \ + --disable-static \ + --enable-shared \ + --enable-vfs-api +RUN make -j "$(nproc)" install + +WORKDIR /tmp +RUN \ + DEB_HOST_MULTIARCH="$(dpkg-architecture --query DEB_HOST_MULTIARCH)" && \ + rm -rf /tmp/libprojfs-release && \ + rm -f /usr/lib/$DEB_HOST_MULTIARCH/libprojfs.la + +RUN apt-get purge -y --auto-remove $BUILD_DEPS diff --git a/docker/Dockerfile-fuse3 b/docker/Dockerfile-fuse3 new file mode 100644 index 0000000..ad7c100 --- /dev/null +++ b/docker/Dockerfile-fuse3 @@ -0,0 +1,36 @@ +FROM debian:stretch + +LABEL org.label-schema.name="projfs-fuse3" +LABEL org.label-schema.description="projfs Linux libfuse v3 image" +LABEL org.label-schema.vendor="GitHub" +LABEL org.label-schema.schema-version="1.0" + +ARG FUSE_VERSION=3.3.0 +ARG FUSE_SHA256=c554863405477cd138c38944be9cdc3781a51d78c369ab878083feb256111b65 + +WORKDIR /tmp + +# fuse3 requires meson 0.42, available from stretch-backports +# wget depends on ca-certificates + +RUN \ + echo 'deb http://deb.debian.org/debian stretch-backports main' >> /etc/apt/sources.list.d/stretch-backports.list && \ + apt-get update -qq && \ + apt-get install -y -qq --no-install-recommends build-essential ca-certificates pkg-config wget udev && \ + apt-get install -y -qq --no-install-recommends -t=stretch-backports meson && \ + rm -rf /var/lib/apt/lists/* + +ENV FUSE_RELEASE "fuse-$FUSE_VERSION" +ENV FUSE_DOWNLOAD "$FUSE_RELEASE.tar.xz" +ENV FUSE_URL_PATH "releases/download/$FUSE_RELEASE/$FUSE_DOWNLOAD" + +RUN \ + wget -q "https://github.com/libfuse/libfuse/$FUSE_URL_PATH" && \ + echo "$FUSE_SHA256 $FUSE_DOWNLOAD" | sha256sum -c - + +RUN tar -xJf "$FUSE_DOWNLOAD" +WORKDIR "$FUSE_RELEASE/build" + +RUN meson --prefix=/usr --sysconfdir=/etc --localstatedir=/var .. +RUN ninja -j "$(nproc)" +RUN ninja install diff --git a/docker/Dockerfile-integrate b/docker/Dockerfile-integrate new file mode 100644 index 0000000..346da4a --- /dev/null +++ b/docker/Dockerfile-integrate @@ -0,0 +1,19 @@ +FROM microsoft/dotnet:2.2-sdk-stretch + +ARG FUSE_VERSION=3.3.0 + +WORKDIR /data/integrate +ENV HOME /data/integrate + +COPY --from=github/fuse3-linux \ + /usr/lib/x86_64-linux-gnu/libfuse3.so.$FUSE_VERSION \ + /usr/lib/x86_64-linux-gnu/ + +RUN set -ex && \ + cd /usr/lib/x86_64-linux-gnu && \ + ln -s libfuse3.so.$FUSE_VERSION libfuse3.so.3 && \ + ln -s libfuse3.so.3 libfuse3.so + +# Allow VFSforGit to load locally-compiled libprojfs binaries for development +ENV LD_LIBRARY_PATH /data/projfs/lib/.libs + diff --git a/docker/Dockerfile-vfs b/docker/Dockerfile-vfs new file mode 100644 index 0000000..4c03f91 --- /dev/null +++ b/docker/Dockerfile-vfs @@ -0,0 +1,5 @@ +FROM microsoft/dotnet:2.2-sdk-stretch + +WORKDIR /data/vfs/src +ENV HOME /data +RUN chmod 0777 /data diff --git a/docker/README.md b/docker/README.md new file mode 100644 index 0000000..17f33df --- /dev/null +++ b/docker/README.md @@ -0,0 +1,136 @@ +# projfs-docker + +Build projfs with Docker. + + - [Getting started](#getting-started) + - [How it works](#how-it-works) + +## Getting started + +``` console +$ ./projfs +usage: ./projfs [command] + +command: + setup + Build all containers and run all build scripts necessary for + development work (excludes distpkg). + + image fuse3|develop|distpkg|vfs|integrate + Build the Docker build environment for the specified component. + + develop configure|make|test|dist|clean + Builds libprojfs for local development and testing. + + configure: runs autogen with appropriate switches + make: runs make + test: runs make test + dist: runs make dist + clean: runs make clean + + vfs restore|make + Build VFSForGit. + + restore: do a NuGet restore + make: build everything + + integrate clone|mount + Run the integration. + + clone: run the MirrorProvider clone + mount: mount the MirrorProvider + + run IMAGE [OPTION ...] -- CMD [ARG ...] + Run a command in the specified image (`fuse3', `develop', `vfs', etc.) + Everything up until `--' is passed as options to `docker run', i.e. + before the image name is given. If no options are specified, defaults + for the image will be used (e.g. enables FUSE for the 'integrate' + image). + + exec IMAGE [OPTION ...] -- CMD [ARG ...] + Run a command in a container already started with `./projfs run'. + + test [--force] + Run the test suite. Will fail if there's already a running integration + container, unless --force is specified, in which case the running + container is stopped before tests are run. +``` + +Here's how to get started: + +``` shell +# Build all images, and run all build scripts. After this finishes, build +# artifacts will be present in your local tree. +./projfs setup + +# Clone the test repository (found at build/PathToMirror). The created +# repository will be at build/integrate/TestRoot. +./projfs integrate clone + +# Run the built-in end-to-end test suite (will clean up after itself if +# successful). +./projfs test + +# Mount the test root. +./projfs integrate mount + +# In another terminal, interact with the created mount. +./projfs exec integrate -- touch TestRoot/src/xyz +./projfs exec integrate -- rm TestRoot/src/xyz + +# Start a shell to play more. (pass `-t' to `docker exec') +./projfs exec integrate -t -- bash +``` + +## How it works + +The command `./projfs image COMPONENT` will build one of five tagged Docker images. + + - `./projfs image fuse3` creates `github/fuse3-linux` from `Dockerfile-fuse3`. It installs development tools and build + prerequisites in a stock Debian stretch image, then fetches and builds FUSE 3.3.0. + + - `./projfs image develop` creates `github/projfs-dev-linux` from `Dockerfile-develop`. It is based on + `github/fuse3-linux`, and installs only the build tools necessary for building libprojfs. + + - `./projfs image distpkg` creates `github/projfs-dist-linux` from `Dockerfile-distpkg`. It is based on + `github/fuse3-linux`. The Dockerfile builds the dist package for libprojfs, then installs it. The resulting image + represents what a clean system with the libprojfs dist package installed should look like. + + - `./projfs image vfs` creates `github/vfs-linux` from `Dockerfile-vfs`. Right now this image is a plain + Microsoft-supplied .NET SDK image with light filesystem modifications for testing. + + - `./projfs image integrate` creates `github/projfs-vfs-linux` from `Dockerfile-integrate`. It uses the .NET SDK image + as base, then copies the built FUSE objects from `github/fuse3-linux` and sets up the environment so libraries from + the ProjFS build (when mounted) will be located correctly. + +Note that each image is only a build environment, and does not contain any source code. Each component has a range of +subcommands which will run commands in containers from each of these images; when doing so, `./projfs` mounts +directories on the Docker host (your local filesystem) into the running containers, so that up-to-date source is seen +and build artifacts are preserved. + +The `projfs` component will mount the root of this repository at `/data/projfs` in the container. `./projfs develop +configure` will run `/data/projfs/autogen.sh` in the container, with the output files being placed on the Docker host. +`./projfs develop make` will then run `make`, with all build artifacts again placed on the Docker host. (You'll have a +bunch of ELF objects which you won't be able to do anything with on macOS.) + +The `vfs` component mounts the source at `/data/vfs/src`, and additional directories in `build` so that NuGet restored +packages and all build output are preserved on the Docker host. + +Finally, the `integrate` component mounts all of the above plus one more (the destination of the MirrorProvider clone +operation), in locations such that everything Just Works™. Build artifacts from each of the previous steps are seen in +the integration container, and paths and environment variables are set appropriately for the whole stack to function. + +Note the filesystem layering that occurs when actually running the integration: + + - `./projfs integrate clone` runs the MirrorProvider clone script, cloning `/data/PathToMirror` (`build/PathToMirror`) + to `/data/integrate/TestRoot` (`build/integrate/TestRoot`). The source and destination of the clone are different + Docker-mounted paths, i.e. both are on your local disk. + + - `./projfs integrate mount` runs the MirrorProvider mount script on `/data/integrate/TestRoot`. `/data/integrate` is + a mount from the host path `build/integrate` in this repo, so the `TestRoot` scaffolding created by the clone + operation still exists. We then mount `/data/integrate/TestRoot/src`. This mount happens in the Docker container, + meaning the container itself accessing `/data/integrate/TestRoot/src` will interact with FUSE. Meanwhile, + `build/integrate/TestRoot/src` will appear as an empty directory on the Docker host. + +We do this to allow rapid iteration without having to rebuild entire containers every time, which is required if the +entire container build process also builds the project. diff --git a/docker/VFSForGit b/docker/VFSForGit new file mode 160000 index 0000000..bc71f5d --- /dev/null +++ b/docker/VFSForGit @@ -0,0 +1 @@ +Subproject commit bc71f5de7b363c0b56b30be29e79ed1b9949b697 diff --git a/docker/build/BuildOutput/.gitignore b/docker/build/BuildOutput/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/docker/build/BuildOutput/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/docker/build/integrate/.gitignore b/docker/build/integrate/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/docker/build/integrate/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/docker/build/packages/.gitignore b/docker/build/packages/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/docker/build/packages/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/docker/cibuild b/docker/cibuild new file mode 100755 index 0000000..1bbfe58 --- /dev/null +++ b/docker/cibuild @@ -0,0 +1,20 @@ +#!/bin/bash + +set -ex + +cd "$(dirname "$0")" + +./projfs image fuse3 + +./projfs image develop +./projfs develop configure +./projfs develop make +./projfs develop test + +./projfs image vfs +./projfs vfs restore +./projfs vfs make + +./projfs image integrate +./projfs integrate clone +./projfs test --force diff --git a/docker/project.rb b/docker/project.rb new file mode 100644 index 0000000..03226aa --- /dev/null +++ b/docker/project.rb @@ -0,0 +1,80 @@ +# frozen_string_literal: true + +class Project + def initialize(name, dockerfile:, image:, mounts: [], commands: {}, run_as_root: false, build_options: [], options: nil) + @name = name + @dockerfile = File.join(File.expand_path(File.dirname(__FILE__)), dockerfile) + @image = image + @mounts = mounts + @commands = commands + @run_as_root = run_as_root + @build_options = build_options + @options = options + end + + def build(quiet: true) + raise "cannot build #@name" if ![@dockerfile, @image].all? + system("docker", "build", *(quiet ? ["-q"] : []), *@build_options, "-f", @dockerfile, "-t", @image, File.dirname(__FILE__)) + end + + def command(name, popen: false) + raise "cannot run command for #@name" if !@image + commands = @commands[name.intern] or raise "command #{name} not found for #@name" + commands = [commands] if commands[0].is_a?(String) + + if popen + raise "cannot popen multiple commands" if commands.length > 1 + run("--", *commands[0], popen: true) + else + commands.each do |command| + run("--", *command) + end + end + end + + def run(*cmd, popen: false) + raise "cannot run #@name" if !@image + argv = [] + argv.concat(@options) if @options && (cmd.length == 0 || cmd[0] == '--') + while cmd.any? && cmd[0] != '--' + argv << cmd.shift + end + @mounts.each { |m| argv << "-v" << m } + argv << @image + cmd.shift if cmd[0] == '--' + argv.concat(cmd) + + args = ["docker", "run", *user_args, "--pid=host", "--rm", "--name", docker_container_name, *argv] + if popen + IO.popen(args, "r+", err: [:child, :out]) + else + system(*args) + end + end + + def exec(*cmd) + raise "cannot exec #@name" if !@image + argv = [] + while cmd.any? && cmd[0] != '--' + argv << cmd.shift + end + argv << docker_container_name + cmd.shift if cmd[0] == '--' + argv.concat(cmd) + system("docker", "exec", *user_args, "-i", *argv) + end + + def docker_container_name + "projfs-#@name" + end + + def user_args + if @run_as_root + [] + else + ["-u", Process.uid.to_s] + end + end +end + +# vim: set sw=2 et: diff --git a/docker/projfs b/docker/projfs new file mode 100755 index 0000000..675aafb --- /dev/null +++ b/docker/projfs @@ -0,0 +1,201 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +require_relative 'project' +require_relative 'tests' + +Dir.chdir(File.dirname(__FILE__)) + +def usage + STDERR.puts <<~USAGE + usage: #$0 [command] + + command: + setup + Build all containers and run all build scripts necessary for + development work (excludes distpkg). + + image fuse3|develop|distpkg|vfs|integrate + Build the Docker build environment for the specified component. + + develop configure|make|test|dist|clean + Builds libprojfs for local development and testing. + + configure: runs autogen with appropriate switches + make: runs make + test: runs make test + dist: runs make dist + clean: runs make clean + + vfs restore|make + Build VFSForGit. + + restore: do a NuGet restore + make: build everything + + integrate clone|mount + Run the integration. + + clone: run the MirrorProvider clone + mount: mount the MirrorProvider + + run IMAGE [OPTION ...] -- CMD [ARG ...] + Run a command in the specified image (`fuse3', `develop', `vfs', etc.) + Everything up until `--' is passed as options to `docker run', i.e. + before the image name is given. If no options are specified, defaults + for the image will be used (e.g. enables FUSE for the 'integrate' + image). + + exec IMAGE [OPTION ...] -- CMD [ARG ...] + Run a command in a container already started with `#$0 run'. + + test [--force] + Run the test suite. Will fail if there's already a running integration + container, unless --force is specified, in which case the running + container is stopped before tests are run. + USAGE + exit 1 +end + +def system(*args) + puts ">> #{args.join " "}" + result = Kernel.system(*args) + if !result + raise "failed to run '#{args.join " "}" + end +end + +usage if ARGV.length == 0 + +fuse3 = Project.new('fuse3', + dockerfile: 'Dockerfile-fuse3', + image: 'github/fuse3-linux' +) + +develop = Project.new('develop', + dockerfile: 'Dockerfile-develop', + image: 'github/projfs-dev-linux', + mounts: ["#{Dir.pwd}/..:/data/projfs"], + commands: { + configure: ['./autogen.sh', '--enable-vfs-api'], + make: ['make'], + test: ['make', 'test'], + dist: ['make', 'dist'], + clean: ['make', 'clean'], + }, + build_options: ['--build-arg', "UID=#{Process.uid}"], + options: ['--device', '/dev/fuse', '--cap-add', 'SYS_ADMIN']) + +distpkg = Project.new('distpkg', + dockerfile: 'Dockerfile-distpkg', + image: 'github/projfs-dist-linux', + mounts: ["#{Dir.pwd}/..:/data/projfs"] +) + +vfs = Project.new('vfs', + dockerfile: 'Dockerfile-vfs', + image: 'github/vfs-linux', + mounts: [ + "#{Dir.pwd}/VFSForGit:/data/vfs/src", + "#{Dir.pwd}/build/packages:/data/vfs/packages", + "#{Dir.pwd}/build/BuildOutput:/data/vfs/BuildOutput", + ], + commands: { + restore: [ + ["dotnet", "restore", "MirrorProvider/MirrorProvider.sln", "/p:Configuration=Debug.Linux", "--packages", "../packages"], + ["dotnet", "restore", "ProjFS.Linux/PrjFSLib.Linux.Managed/PrjFSLib.Linux.Managed.csproj", "/p:Configuration=Debug", "/p:Platform=x64", "--packages", "../packages"], + ], + make: [ + ["dotnet", "build", "ProjFS.Linux/PrjFSLib.Linux.Managed/PrjFSLib.Linux.Managed.csproj", "/p:Configuration=Debug", "/p:Platform=x64", "--no-restore"], + ["dotnet", "build", "MirrorProvider/MirrorProvider.sln", "/p:Configuration=Debug.Linux", "--no-restore"], + ] + }) + +integrate = Project.new('integrate', + dockerfile: 'Dockerfile-integrate', + image: 'github/projfs-vfs-linux', + mounts: [ + "#{Dir.pwd}/..:/data/projfs", + "#{Dir.pwd}/VFSForGit:/data/vfs/src", + "#{Dir.pwd}/build/packages:/data/vfs/packages", + "#{Dir.pwd}/build/BuildOutput:/data/vfs/BuildOutput", + "#{Dir.pwd}/build/PathToMirror:/data/PathToMirror", + "#{Dir.pwd}/build/integrate:/data/integrate" + ], + commands: { + clone: [["env", "PATH_TO_MIRROR=/data/PathToMirror", "TEST_ROOT=/data/integrate/TestRoot", "/data/vfs/src/MirrorProvider/Scripts/Linux/MirrorProvider_Clone.sh"]], + mount: [["env", "TEST_ROOT=/data/integrate/TestRoot", "/data/vfs/src/MirrorProvider/Scripts/Linux/MirrorProvider_Mount.sh"]], + }, + options: ['--device', '/dev/fuse', '--cap-add', 'SYS_ADMIN', '-i'], + run_as_root: true) + +images = { + "fuse3" => fuse3, + "develop" => develop, + "distpkg" => distpkg, + "vfs" => vfs, + "integrate" => integrate, +} + +case ARGV.shift +when "setup" + usage if ARGV.length > 0 + fuse3.build(quiet:false) + + develop.build(quiet: false) + develop.command "configure" + develop.command "make" + develop.command "test" + + vfs.build(quiet: false) + vfs.command "restore" + vfs.command "make" + + integrate.build(quiet: false) + +when "image" + usage if ARGV.length != 1 + images[ARGV[0]].build(quiet: false) + +when "develop" + usage if ARGV.length != 1 + develop.command(ARGV[0]) + +when "vfs" + usage if ARGV.length != 1 + vfs.command(ARGV[0]) + +when "integrate" + usage if ARGV.length != 1 + integrate.command(ARGV[0]) + +when "run" + index = ARGV.index("--") + usage if ARGV.length < 3 || index == nil || index == ARGV.length - 1 + + image = images[ARGV[0]] || usage + image.run(*ARGV[1..-1]) + +when "exec" + index = ARGV.index("--") + usage if ARGV.length < 3 || index == nil || index == ARGV.length - 1 + + image = images[ARGV[0]] || usage + image.exec(*ARGV[1..-1]) + +when "test" + force = false + while arg = ARGV.shift + case arg + when "--force" then force = true + else usage + end + end + + tests(images, force: force) + +else + usage +end + +# vim: set sw=2 et: diff --git a/docker/tests.rb b/docker/tests.rb new file mode 100644 index 0000000..6fe6c6c --- /dev/null +++ b/docker/tests.rb @@ -0,0 +1,74 @@ +# frozen_string_literal: true + +def report(name, text) + text.split("\n").each do |line| + puts "#{name}: #{line}" + end +end + +def wait_for(name, io, msgs, timeout: 1) + buf = String.new + start = Time.now + + while !(msg = msgs.find {|m| buf.include?(m)}) + begin + s = io.read_nonblock(1024) + rescue IO::WaitReadable + remaining = timeout - (Time.now - start) + raise "timed out waiting for #{msg.inspect}" if remaining <= 0 + any = IO.select([io], nil, nil, remaining) + retry if any + raise "timed out waiting for one of #{msgs.inspect}" + rescue EOFError + raise "timed out waiting for one of #{msgs.inspect} (got EOF)" + end + + report(name, s) + buf << s + end + + msg +end + +MSG_PRESS_ENTER = "Press Enter to end" +MSG_CONFLICT = "Conflict. The container name" + +def tests(images, force: false) + integration = images["integrate"].command("mount", popen: true) + msg = wait_for("integrate", integration, [MSG_PRESS_ENTER, MSG_CONFLICT], timeout: 5) + if msg == MSG_CONFLICT + puts "integration container already running" + system "docker", "ps", "-f", "name=#{images["integrate"].docker_container_name}" + if !force + raise "cannot run tests; remove container or do so automatically with --force" + end + + system "docker", "stop", "-t", "0", images["integrate"].docker_container_name + + integration = images["integrate"].command("mount", popen: true) + msg = wait_for("integrate", integration, [MSG_PRESS_ENTER, MSG_CONFLICT], timeout: 5) + if msg == MSG_CONFLICT + raise "still couldn't start container" + end + end + + puts "test: checking that touching a file is recognised" + + id = 16.times.map { ("a".."z").to_a.sample(1) }.join + images["integrate"].exec("--", "touch", "TestRoot/src/#{id}") + msg = wait_for("integrate", integration, ["OnNewFileCreated (isDirectory: False): #{id}"]) + images["integrate"].exec("--", "rm", "TestRoot/src/#{id}") + msg = wait_for("integrate", integration, ["OnPreDelete (isDirectory: False): #{id}"]) + + puts "test: finished; stopping mount gracefully" + + integration.write "\n" + integration.flush + integration.close_write + report("integrate", integration.read) + integration.close + + puts "test: done" +end + +# vim: set sw=2 et: diff --git a/docker/update-readme b/docker/update-readme new file mode 100755 index 0000000..428784c --- /dev/null +++ b/docker/update-readme @@ -0,0 +1,18 @@ +#!/usr/bin/env ruby + +require 'commonmarker' + +doc = CommonMarker.render_doc(File.read('README.md')) + +did_replace = false + +doc.walk do |node| + if !did_replace && node.type == :code_block && node.string_content.start_with?("$ ./projfs") + node.string_content = "$ ./projfs\n" + `./projfs 2>&1` + did_replace = true + end +end + +raise "replaced nothing" unless did_replace + +File.open("README.md", "w") {|f| f.write(doc.to_commonmark)} diff --git a/script/cibuild b/script/cibuild deleted file mode 100755 index b84c26f..0000000 --- a/script/cibuild +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/sh - -cat <