diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9e0cc3e..2a02e1b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -49,6 +49,25 @@ jobs: # The library crates have no OS dependency and run on the host. The # parser, the graph, the restart policy, the log format and the calendar # arithmetic are where the logic errors are. + # `test-demo` builds the image locally and checks it, which is what let + # the README ship a `docker run ghcr.io/...` line pointing at something + # that had never been published: the test and the README were talking + # about different images. This checks they agree on the name, which is + # the half a test on a locally-built image structurally cannot. + - name: The README's image reference is the one CI publishes + run: | + OWNER=$(echo "${{ github.repository_owner }}" | tr '[:upper:]' '[:lower:]') + WANT="ghcr.io/$OWNER/oxinit:demo" + if ! grep -qF "$WANT" README.md; then + echo "README.md does not mention $WANT, which is what CI pushes" + exit 1 + fi + if ! grep -qF 'oxinit:demo' .github/workflows/ci.yml; then + echo "the publish step no longer pushes the tag the README names" + exit 1 + fi + echo "README and CI agree on $WANT" + - name: Host tests run: cargo test -p oxinit-unit -p oxinit-graph -p oxinit-service -p oxinit-cgroup -p oxinit-user -p oxinit-ipc -p oxinit-log -p oxinit-timer @@ -164,6 +183,9 @@ jobs: name: the README's demo runs-on: ubuntu-latest needs: check + permissions: + contents: read + packages: write steps: - uses: actions/checkout@v4 - run: rustup target add x86_64-unknown-linux-musl @@ -185,6 +207,19 @@ jobs: # works is worse than a README with nothing in it. - run: cargo xtask test-demo + # The demo image is documentation, not a release artifact, so it is + # published from `main` rather than from a tag. Tying it to a release + # meant the README told people to pull something that would not exist + # until a version number had been declared — which is a separate + # decision, and was not going to be made that week. + - name: Publish the demo image + if: github.ref == 'refs/heads/main' && github.event_name == 'push' + run: | + OWNER=$(echo "${{ github.repository_owner }}" | tr '[:upper:]' '[:lower:]') + echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin + docker tag oxinit:demo "ghcr.io/$OWNER/oxinit:demo" + docker push "ghcr.io/$OWNER/oxinit:demo" + distro: name: a real userspace runs-on: ubuntu-latest diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ee1e882..f3a8106 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -81,18 +81,20 @@ jobs: # demo/, not units/ — the latter is the test suite and contains services # that fail on purpose. - name: Build the demo image - run: cargo xtask demo --build-only --tag ghcr.io/${{ github.repository_owner }}/oxinit:demo + run: cargo xtask demo --build-only - name: Log in to ghcr.io run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin + # Only the versioned tag. `:demo` tracks `main` and is published by CI, + # because it is documentation rather than a release artifact — the README + # points at it, and a README should not be waiting on a version number + # somebody has not decided to declare yet. - name: Push run: | OWNER=$(echo "${{ github.repository_owner }}" | tr '[:upper:]' '[:lower:]') - docker tag ghcr.io/${{ github.repository_owner }}/oxinit:demo ghcr.io/$OWNER/oxinit:demo - docker tag ghcr.io/$OWNER/oxinit:demo ghcr.io/$OWNER/oxinit:${{ github.ref_name }} - docker push ghcr.io/$OWNER/oxinit:demo - docker push ghcr.io/$OWNER/oxinit:${{ github.ref_name }} + docker tag oxinit:demo "ghcr.io/$OWNER/oxinit:${{ github.ref_name }}" + docker push "ghcr.io/$OWNER/oxinit:${{ github.ref_name }}" release: name: publish @@ -125,6 +127,12 @@ jobs: docker run --rm --name oxinit-demo -p 8080:8080 ghcr.io/OWNER/oxinit:TAG ``` + `ghcr.io/OWNER/oxinit:demo` tracks `main` if you would rather have the + latest. + + ```bash + ``` + ### Install Download the tarball for your architecture below, check it, and put diff --git a/README.md b/README.md index 49733f9..586740f 100644 --- a/README.md +++ b/README.md @@ -30,10 +30,17 @@ flowchart TD That is deliberate: a bug in the log writer kills the log writer, and a bug in PID 1 kills the machine. -## Try it — one command +## Try it -You need [Docker](https://docs.docker.com/get-started/get-docker/) and nothing -else. No Rust, no virtual machine, no kernel. +You need [Docker](https://docs.docker.com/get-started/get-docker/) and a Rust +toolchain. Two commands: + +```bash +cargo xtask fetch --arch x86_64 # a kernel and a busybox, into target/ +cargo xtask demo # build the image and run it +``` + +Or pull the image, which tracks `main` and needs no toolchain at all: ```bash docker run --rm --name oxinit-demo -p 8080:8080 ghcr.io/youhide/oxinit:demo @@ -81,14 +88,8 @@ for each to actually be gone, and exits `0`. Most container images have to be killed after a ten-second grace period; that shows up as exit code 137. The units the demo runs are in [demo/](demo/), one small file each, and they -are meant to be read. - -To build and run it yourself instead of pulling the published image: - -```bash -cargo xtask fetch --arch x86_64 # a kernel and a busybox, into target/ -cargo xtask demo # build the image and run it -``` +are meant to be read. The image is 4.6 MB — statically linked against musl, +`FROM scratch`, no base image and no shared libraries. ## Why diff --git a/ROADMAP.md b/ROADMAP.md index 8f229ba..fa439fe 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -963,6 +963,39 @@ twelve seconds later — and the test image asks for a shutdown sixteen seconds in, inside that window with margin on both sides. Reproducing it by luck once is not a regression test. +### The first command in the README did not work + +Reported by the first person to try it, which is the whole point of writing a +section for people who have not seen the project before — and an unambiguous +failure of the milestone that added it. + +The README told a newcomer to `docker run ghcr.io/…/oxinit:demo`. No tag had +been pushed, so the release workflow that published that image had never run, +and the image did not exist. `test-demo` passed the entire time: it builds the +image locally and checks *that*, so the test and the README were talking about +two different images and nothing compared them. + +Three fixes, and only one of them is the obvious one. + +The demo image is **documentation, not a release artifact**, so it is published +from `main` by CI rather than from a tag by the release workflow. Tying it to a +release meant the README pointed at something that would not exist until a +version number had been declared — a separate decision, and not one anybody was +about to make. The release workflow now adds a versioned tag and nothing else. + +The README leads with the two commands that work with no registry at all, and +offers the pull second. + +And CI checks that the image reference in the README is the one the publish +step pushes. That is the half a test on a locally built image structurally +cannot cover, and it is the half that failed. + +One thing here is not automatable: a package pushed by Actions is **private by +default**, and an anonymous `docker run` against a private package fails with +exactly the `denied` that was reported. Making it public is a one-time setting +on the repository's package page, and it belongs to whoever owns the account +rather than to CI. + ## Not doing, and why These were on the list. They are coming off it with a reason rather than