GitHub actions are workflows triggered by GitHub events. They are quite flexible, they run by default in "bare" operating system runners, but, additionally, they can be hosted in user-provided containers. The problem is, GitHub expect these containers to have certain services and structure, which is a problem if you use a standard or plain container.
Through the creation of this container, and multiple questions in the GitHub community, I've kinda discovered what are the things needed for containers to be used seamlessly in workflows. Here're the tips:
GHA user is UID=1001
While the first user you will create in any Linux system will have UID equal to 1000, GHAs are a bit special and they use UID 1001. So you'd better use that UID for your user, as is done in this Raku container
FROM alpine:latest as base
ARG RAKU_RELEASE=2021.12
ENV PKGS="git make gcc musl-dev perl linux-headers bash"
RUN apk update && apk upgrade \
&& apk add --no-cache $PKGS \
&& git clone --depth 1 --branch ${RAKU_RELEASE} https://github.com/MoarVM/MoarVM.git \
&& cd MoarVM \
&& perl Configure.pl --prefix /usr \
&& make --print-data-base \
&& make install\
&& cd .. \
&& git clone --depth 1 --branch ${RAKU_RELEASE} git://github.com/Raku/nqp.git \
&& cd nqp \
&& perl Configure.pl --backends=moar --prefix /usr \
&& make install \
&& cd .. \
&& git clone --depth 1 --branch ${RAKU_RELEASE} https://github.com/rakudo/rakudo.git \
&& cd rakudo \
&& perl Configure.pl --backends=moar --prefix /usr \
&& make install \
&& ls /usr/share/nqp/
FROM alpine:latest
ARG UID=1000
LABEL version="0.5.0" maintainer="JJMerelo@GMail.com" raku_release=${RAKU_RELEASE} raku_user_uid=${UID}
COPY --from=base /usr/lib/libmoar.so /usr/lib
COPY --from=base /usr/share/nqp/ /usr/share/nqp
COPY --from=base /usr/share/perl6/ /usr/share/perl6
COPY --from=base /usr/bin/moar /usr/bin/nqp /usr/bin/raku /usr/bin/perl6 /usr/bin/rakudo /usr/bin/
RUN mkdir /github \
&& addgroup -S raku && adduser -S raku -G raku --uid ${UID}
USER raku
WORKDIR /home/raku
ENTRYPOINT ["raku"]
It's a bit longish, but the real deal is just above these lines. You can use that UID by default, but in my case I wanted two versions, just in case I needed more modifications later for GHS
Spoiler: I did
So I use an ARG
that carries the UID, with 1000 by default, and there's a specific build-arg
for GHAs. And that's that.
Is that all? Not really.
GHAs reset HOME
The container mounts a directory into /github/home
and then calls it $HOME
. Don't really know why, but it does. This can wreak a bit of havoc in languages for containers that expect home to be where it was originally used to install whatever. That can be easily fixed by just setting HOME
manually at a job level, but it can also be fixed adjusting the search path for installed modules, which in the case of Raku means setting RAKULIB
, as done here:
FROM ghcr.io/jj/raku-gha
ENV PKGS="git tar" PKGS_TMP="make gcc linux-headers musl-dev" WORKDIR="/home/raku"
LABEL version="1.0.3" maintainer="JJMerelo@GMail.com" rakuversion=$VER
USER root
RUN apk update && apk upgrade && apk add --no-cache $PKGS $PKGS_TMP
USER raku
# Environment
ENV PATH="${WORKDIR}/.raku/bin:${WORKDIR}/.raku/share/perl6/site/bin:${PATH}" \
ENV="${WORKDIR}/.profile"\
RAKULIB="inst#/home/raku/.raku"
# Basic setup, programs and init
WORKDIR $WORKDIR
RUN git clone --depth 1 https://github.com/ugexe/zef.git \
&& cd zef && raku -I. bin/zef install . \
&& zef install Linenoise \
&& cd .. && rm -rf zef
USER root
RUN apk del $PGKS_TMP
USER raku
ENTRYPOINT ["raku"]
This is already GHA-specific, as revealed by the base image. $RAKULIB
is defined midway through the file, with the inst#
prefix indicating it's already precompiled and ready to go. Raku will search there first, and then proceed to the supposed place, inst#/github/home/.raku
. Where there will be noting in the raw container. Will it always be empty? Not by a long shot, because it will be used to install distributions by Raku, and thus it will be the place to cache. But something else is needed first.
Check also that we're including
git
in this image. Also useful for the checkout action (although this one has all kinds of defaults, and it's not actually essential).
You need to install GNU tar first
Alpine is an amazing little distro for containers, but part of its appeal comes from the fact that it puts a lot of external utilities into BusyBox, and that includes tar
. However, in an apparently undocumented move, the caching GHA uses BSD or GNU tar for storing and restoring artifacts. That means it's needed in your container. Check above:
ENV PKGS="git tar" PKGS_TMP="make gcc linux-headers musl-dev"
// and later
RUN apk update && apk upgrade && apk add --no-cache $PKGS
By installing tar
, the image will be a bit heavier (in this case, taking 10 seconds to download and start), but not really a big deal.
And you're ready to go
See it in action here. Once the container is GHA-ready, you can run (at least these) Github actions in your workflow as easily as if you were running the default base runner.
Top comments (2)
This is useful Juan. 👍
Glad you liked it. Thanks for reaching out.