No input of the build action takes a token, a registry or a package server. A
project whose dependencies do not resolve from the default registries needs them
arranged in a step of its own beforehand, and everything Julia reads from the
environment is available to the build through the step's env.
---
name: "Distribution"
on:
pull_request:
permissions:
contents: "read"
jobs:
build-distribution:
name: "Build the distribution"
# At job level so that every step resolving packages uses it, the registry
# step below included rather than the build alone.
env:
JULIA_PKG_USE_CLI_GIT: "true"
runs-on: "ubuntu-latest"
# A distribution build is long enough that a wedged one is worth failing
# rather than leaving to the six-hour default.
timeout-minutes: 60
steps:
- name: "Check out the source"
uses: "actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1" # v7.0.1
- name: "Install Julia"
uses: "julia-actions/install-juliaup@1cc1b0ecfb06c04ff09fe24cab1476106460750e" # v3.1.5
with:
channel: "1.12.6"
# `insteadOf` is honored by the `git` command line rather than by the
# `libgit2` Julia clones with by default, which is what the job's
# `JULIA_PKG_USE_CLI_GIT` switches. A token in the URL is written into the
# runner's global git configuration, so it is readable by anything that
# runs afterwards.
- name: "Add a private registry"
env:
REGISTRY_TOKEN: "${{ secrets.REGISTRY_TOKEN }}"
run: |
set -euo pipefail
git config --global \
url."https://x-access-token:${REGISTRY_TOKEN}@github.com/".insteadOf \
"https://github.com/"
julia -e 'using Pkg
Pkg.Registry.add("General")
Pkg.Registry.add(Pkg.RegistrySpec(url = "https://github.com/your-org/YourRegistry.git"))'
- name: "Build the distribution"
id: "build-distribution"
uses: "JuliaComputing/create-julia-distribution@958799562b82fcabd2f5508f7ee296e1d91de200"
with:
distribution-id: "my-product-linux-x86_64"
- name: "Pack the distribution"
id: "pack-distribution"
uses: "JuliaComputing/create-julia-distribution/pack@958799562b82fcabd2f5508f7ee296e1d91de200"
with:
distribution-path: "${{ steps.build-distribution.outputs.distribution-path }}"The build action is a composite action, so env on the step that calls it is in
scope for the steps inside it, and a variable set at job level is in scope for
all of them. Nothing needs an input for the build to see
JULIA_PKG_USE_CLI_GIT, JULIA_DEPOT_PATH or anything else Julia reads.
Registries are per-depot rather than per-step, so a registry added in one step
is still there for the build in a later one as long as the depot is the same —
which it is, unless a step changes JULIA_DEPOT_PATH.
Dependencies served by a package server need no registry step at all, only the variable that names it, on the build step or on the job:
- name: "Build the distribution"
id: "build-distribution"
env:
JULIA_PKG_SERVER: "packages.example.com"
uses: "JuliaComputing/create-julia-distribution@958799562b82fcabd2f5508f7ee296e1d91de200"
with:
distribution-id: "my-product-linux-x86_64"A server requiring authentication is reached through the token file Julia
expects in the depot, written by a step before the build. If that server also
hosts the registry,
PumasAI/add-private-registry
does both halves in one step, and is shorter than doing either by hand:
- name: "Add the private registry"
uses: "PumasAI/add-private-registry@3ee6e5689774ff51631dd6b1fd35bae37b363ae3" # v1.0.1
env:
JULIA_PKG_SERVER: "packages.example.com"
with:
juliahub_token_encoded: "${{ secrets.PACKAGE_SERVER_TOKEN }}"
private_registry_name: "YourRegistry"
private_registry_uuid: "00000000-0000-0000-0000-000000000000"Its juliahub_token_encoded input reads narrower than the action behaves. What
it wants is a base64-encoded auth.toml — the whole document Julia expects,
rather than a bare token — and what it does with it is write that file into the
depot for whichever server JULIA_PKG_SERVER names, which is where Julia looks
for it. Any package server you can produce an auth.toml for is therefore in
scope, whatever the input is called.
The step earlier in this workflow is the case that action does not cover: a registry that is a plain git repository, reached with a git credential rather than a package-server token.
A Manifest.toml referring to a git repository using SSH rather than a
registered version needs the clone to succeed rather than the registry lookup.
webfactory/ssh-agent starts an
agent, loads the key and exports the socket for the steps that follow, which is
otherwise a fiddly block of shell:
- name: "Authorize SSH access to private dependencies"
uses: "webfactory/ssh-agent@e83874834305fe9a4a2997156cb26c5de65a8555" # v0.10.0
with:
ssh-private-key: "${{ secrets.DEPENDENCY_SSH_KEY }}"The job's JULIA_PKG_USE_CLI_GIT matters here too: the libgit2 Julia clones
with by default cannot talk to that agent, while the git command line can.
Building a system image runs the code being built into it: precompilation, module initialization, and whatever else is evaluated while the image is created. A credential in scope for the build is in scope for that code, whether it is in the environment, in the global git configuration or in a file the earlier step wrote.
Two things follow. Credentials the resolve genuinely needs have no way around being present, so the narrower they are the better — a read-only token, an account that reaches the registry and nothing else. Credentials the build does not need are best granted after it, in the interval between the build and the pack, which is where the signing example writes its signing certificate.