Skip to content

Commit 8d916d7

Browse files
committed
Release reproducible visualization starter
1 parent bdb61ea commit 8d916d7

5 files changed

Lines changed: 3755 additions & 48 deletions

File tree

.github/workflows/starter-environments.yml

Lines changed: 59 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ on:
1414
workflow_dispatch:
1515
inputs:
1616
target:
17-
description: Starter environment to validate (all is required to publish)
17+
description: Starter environment to build (select one to publish only that image)
1818
required: true
1919
default: auto
2020
type: choice
@@ -147,8 +147,8 @@ jobs:
147147
;;
148148
esac
149149
150-
if [[ "$REQUESTED_PUBLISH" == "true" && "$REQUESTED_TARGET" != "all" ]]; then
151-
echo "Publishing requires target=all so every image is validated together." >&2
150+
if [[ "$REQUESTED_PUBLISH" == "true" && "$REQUESTED_TARGET" == "auto" ]]; then
151+
echo "Publishing requires an explicit target (or all); auto has no deterministic release scope." >&2
152152
exit 1
153153
fi
154154
@@ -366,68 +366,98 @@ jobs:
366366
retention-days: 90
367367

368368
- name: Log in to GHCR
369-
if: ${{ github.event_name == 'workflow_dispatch' && inputs.publish && inputs.target == 'all' }}
369+
if: ${{ github.event_name == 'workflow_dispatch' && inputs.publish }}
370370
uses: docker/login-action@v3
371371
with:
372372
registry: ghcr.io
373373
username: ${{ github.actor }}
374374
password: ${{ secrets.GITHUB_TOKEN }}
375375

376376
- name: Publish starter images
377-
if: ${{ github.event_name == 'workflow_dispatch' && inputs.publish && inputs.target == 'all' }}
377+
if: ${{ github.event_name == 'workflow_dispatch' && inputs.publish }}
378+
env:
379+
PUBLISH_BASE: ${{ steps.targets.outputs.base }}
380+
PUBLISH_PATHWAY: ${{ steps.targets.outputs.pathway }}
381+
PUBLISH_VISUALIZATION: ${{ steps.targets.outputs.visualization }}
382+
PUBLISH_STATISTICS: ${{ steps.targets.outputs.statistics }}
378383
run: |
384+
set -euo pipefail
385+
379386
base_version="$(tr -d '[:space:]' < starter-environments/r-base/VERSION)"
380387
pathway_version="$(tr -d '[:space:]' < starter-environments/r-pathway/VERSION)"
381388
visualization_version="$(tr -d '[:space:]' < starter-environments/r-visualization/VERSION)"
382389
statistics_version="$(tr -d '[:space:]' < starter-environments/r-statistics/VERSION)"
383-
docker tag omix-r-base:ci ghcr.io/nidap-community/omix-r-base:${{ github.sha }}
384-
docker tag omix-r-base:ci ghcr.io/nidap-community/omix-r-base:"$base_version"
385-
docker tag omix-r-pathway:ci ghcr.io/nidap-community/omix-r-pathway:${{ github.sha }}
386-
docker tag omix-r-pathway:ci ghcr.io/nidap-community/omix-r-pathway:"$pathway_version"
387-
docker push ghcr.io/nidap-community/omix-r-base:${{ github.sha }}
388-
docker push ghcr.io/nidap-community/omix-r-base:"$base_version"
389-
docker push ghcr.io/nidap-community/omix-r-pathway:${{ github.sha }}
390-
docker push ghcr.io/nidap-community/omix-r-pathway:"$pathway_version"
391-
if [[ "$visualization_version" == *-v0 ]]; then
392-
echo "r-visualization is still a bootstrap definition; skipping publication until its CI-captured renv.lock is committed and VERSION is v1 or later."
393-
else
390+
391+
if [[ "$PUBLISH_BASE" == "true" ]]; then
392+
docker tag omix-r-base:ci ghcr.io/nidap-community/omix-r-base:${{ github.sha }}
393+
docker tag omix-r-base:ci ghcr.io/nidap-community/omix-r-base:"$base_version"
394+
docker push ghcr.io/nidap-community/omix-r-base:${{ github.sha }}
395+
docker push ghcr.io/nidap-community/omix-r-base:"$base_version"
396+
fi
397+
398+
if [[ "$PUBLISH_PATHWAY" == "true" ]]; then
399+
docker tag omix-r-pathway:ci ghcr.io/nidap-community/omix-r-pathway:${{ github.sha }}
400+
docker tag omix-r-pathway:ci ghcr.io/nidap-community/omix-r-pathway:"$pathway_version"
401+
docker push ghcr.io/nidap-community/omix-r-pathway:${{ github.sha }}
402+
docker push ghcr.io/nidap-community/omix-r-pathway:"$pathway_version"
403+
fi
404+
405+
if [[ "$PUBLISH_VISUALIZATION" == "true" ]]; then
406+
if [[ "$visualization_version" == *-v0 ]]; then
407+
echo "r-visualization is still a bootstrap definition; commit its CI-captured renv.lock and change VERSION to v1 or later before publishing." >&2
408+
exit 1
409+
fi
394410
docker tag omix-r-visualization:ci ghcr.io/nidap-community/omix-r-visualization:${{ github.sha }}
395411
docker tag omix-r-visualization:ci ghcr.io/nidap-community/omix-r-visualization:"$visualization_version"
396412
docker push ghcr.io/nidap-community/omix-r-visualization:${{ github.sha }}
397413
docker push ghcr.io/nidap-community/omix-r-visualization:"$visualization_version"
398414
fi
399-
if [[ "$statistics_version" == *-v0 ]]; then
400-
echo "r-statistics is still a bootstrap definition; skipping publication until its CI-captured renv.lock is committed and VERSION is v1 or later."
401-
else
415+
416+
if [[ "$PUBLISH_STATISTICS" == "true" ]]; then
417+
if [[ "$statistics_version" == *-v0 ]]; then
418+
echo "r-statistics is still a bootstrap definition; commit its CI-captured renv.lock and change VERSION to v1 or later before publishing." >&2
419+
exit 1
420+
fi
402421
docker tag omix-r-statistics:ci ghcr.io/nidap-community/omix-r-statistics:${{ github.sha }}
403422
docker tag omix-r-statistics:ci ghcr.io/nidap-community/omix-r-statistics:"$statistics_version"
404423
docker push ghcr.io/nidap-community/omix-r-statistics:${{ github.sha }}
405424
docker push ghcr.io/nidap-community/omix-r-statistics:"$statistics_version"
406425
fi
407426
408427
- name: Record published image digests
409-
if: ${{ github.event_name == 'workflow_dispatch' && inputs.publish && inputs.target == 'all' }}
428+
if: ${{ github.event_name == 'workflow_dispatch' && inputs.publish }}
429+
env:
430+
PUBLISH_BASE: ${{ steps.targets.outputs.base }}
431+
PUBLISH_PATHWAY: ${{ steps.targets.outputs.pathway }}
432+
PUBLISH_VISUALIZATION: ${{ steps.targets.outputs.visualization }}
433+
PUBLISH_STATISTICS: ${{ steps.targets.outputs.statistics }}
410434
run: |
435+
set -euo pipefail
436+
411437
base_version="$(tr -d '[:space:]' < starter-environments/r-base/VERSION)"
412438
pathway_version="$(tr -d '[:space:]' < starter-environments/r-pathway/VERSION)"
413439
visualization_version="$(tr -d '[:space:]' < starter-environments/r-visualization/VERSION)"
414440
statistics_version="$(tr -d '[:space:]' < starter-environments/r-statistics/VERSION)"
415-
base_ref="ghcr.io/nidap-community/omix-r-base:$base_version"
416-
pathway_ref="ghcr.io/nidap-community/omix-r-pathway:$pathway_version"
417-
statistics_ref="ghcr.io/nidap-community/omix-r-statistics:$statistics_version"
418-
base_digest="$(docker buildx imagetools inspect "$base_ref" --format '{{.Manifest.Digest}}')"
419-
pathway_digest="$(docker buildx imagetools inspect "$pathway_ref" --format '{{.Manifest.Digest}}')"
420441
{
421442
echo '## Published OMIX starter environments'
422443
echo
423-
echo "- \`$base_ref@$base_digest\`"
424-
echo "- \`$pathway_ref@$pathway_digest\`"
425-
if [[ "$visualization_version" != *-v0 ]]; then
444+
if [[ "$PUBLISH_BASE" == "true" ]]; then
445+
base_ref="ghcr.io/nidap-community/omix-r-base:$base_version"
446+
base_digest="$(docker buildx imagetools inspect "$base_ref" --format '{{.Manifest.Digest}}')"
447+
echo "- \`$base_ref@$base_digest\`"
448+
fi
449+
if [[ "$PUBLISH_PATHWAY" == "true" ]]; then
450+
pathway_ref="ghcr.io/nidap-community/omix-r-pathway:$pathway_version"
451+
pathway_digest="$(docker buildx imagetools inspect "$pathway_ref" --format '{{.Manifest.Digest}}')"
452+
echo "- \`$pathway_ref@$pathway_digest\`"
453+
fi
454+
if [[ "$PUBLISH_VISUALIZATION" == "true" ]]; then
426455
visualization_ref="ghcr.io/nidap-community/omix-r-visualization:$visualization_version"
427456
visualization_digest="$(docker buildx imagetools inspect "$visualization_ref" --format '{{.Manifest.Digest}}')"
428457
echo "- \`$visualization_ref@$visualization_digest\`"
429458
fi
430-
if [[ "$statistics_version" != *-v0 ]]; then
459+
if [[ "$PUBLISH_STATISTICS" == "true" ]]; then
460+
statistics_ref="ghcr.io/nidap-community/omix-r-statistics:$statistics_version"
431461
statistics_digest="$(docker buildx imagetools inspect "$statistics_ref" --format '{{.Manifest.Digest}}')"
432462
echo "- \`$statistics_ref@$statistics_digest\`"
433463
fi
Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
# The bootstrap definition is deliberately tagged `v0` and must not be
2-
# published. CI captures its fully resolved lockfile; the first release will
3-
# restore that lockfile and use a `v1` version tag.
41
ARG BASE_IMAGE=ghcr.io/nidap-community/omix-r-base:r4.4.3-v1
52
FROM ${BASE_IMAGE}
63

@@ -14,13 +11,12 @@ RUN apt-get update \
1411
libuv1-dev \
1512
&& rm -rf /var/lib/apt/lists/*
1613

17-
RUN R -q -e "packages <- c( \
18-
'dplyr', 'tidyr', 'ggplot2', 'ggrepel', 'ggbeeswarm', 'broom', \
19-
'multcomp', 'multcompView', 'RColorBrewer', 'stringr', 'optparse', \
20-
'patchwork', 'pheatmap', 'plotly' \
21-
); \
22-
install.packages(packages, repos = 'https://cloud.r-project.org'); \
23-
missing <- packages[!vapply(packages, requireNamespace, logical(1), quietly = TRUE)]; \
24-
if (length(missing)) stop('Required visualization package(s) failed to install: ', paste(missing, collapse = ', '))"
25-
2614
WORKDIR /workspace
15+
16+
COPY renv.lock ./renv.lock
17+
18+
RUN R -q -e "renv::restore( \
19+
lockfile = 'renv.lock', \
20+
library = .libPaths()[1], \
21+
prompt = FALSE \
22+
)"

starter-environments/r-visualization/README.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,14 @@ The image contains the plotting stack (`ggplot2`, `dplyr`, `tidyr`, `plotly`,
1414

1515
## Release contract
1616

17-
This OMIX-owned bootstrap definition uses the immutable base image
18-
`omix-r-base:r4.4.3-v1`. It verifies that every declared package is actually
17+
This OMIX-owned runtime uses the immutable base image
18+
`omix-r-base:r4.4.3-v1` and the CI-captured `renv.lock` to restore its R
19+
dependencies reproducibly. It verifies that every declared package is actually
1920
loadable, including `plotly`; the completed legacy image exposed a missing
2021
`plotly` installation despite declaring it. It installs `cmake` and
2122
`libuv1-dev` locally because `plotly`'s `htmlwidgets` dependency chain needs
2223
the `fs` package, which requires a usable `libuv` build path.
2324

24-
The initial `r4.4.3-v0` version is validation-only and cannot be published.
25-
Its CI artifact will provide the fully resolved `renv.lock`. Commit that lock,
26-
replace the bootstrap installation step with `renv::restore()`, and change the
27-
version to `r4.4.3-v1` before the first OMIX-owned release.
25+
The first OMIX-owned release is `r4.4.3-v1`. Every later dependency update must
26+
produce a new CI-verified lockfile and a new image version. Capsules and other
27+
consumers must pin an immutable published image tag or digest, never `latest`.
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
r4.4.3-v0
1+
r4.4.3-v1

0 commit comments

Comments
 (0)