Skip to content

Build OCI image to use CLI in containers#208

Merged
david-waltermire merged 25 commits intodevelopfrom
22-publish-docker-container
Jan 8, 2026
Merged

Build OCI image to use CLI in containers#208
david-waltermire merged 25 commits intodevelopfrom
22-publish-docker-container

Conversation

@aj-stein
Copy link
Copy Markdown
Contributor

@aj-stein aj-stein commented Dec 14, 2025

Committer Notes

Closes #22.

All Submissions:

By submitting a pull request, you are agreeing to provide this contribution under the CC0 1.0 Universal public domain dedication.

Changes to Core Features:

  • Have you added an explanation of what your changes do and why you'd like us to include them?
  • Have you written new tests for your core changes, as applicable?
  • Have you included examples of how to use your new feature(s)?
  • Have you updated all website and readme documentation affected by the changes you made?

Summary by CodeRabbit

  • New Features

    • Builds and publishes multi-architecture container images with optional registry push and attestations; images verify the CLI at startup.
  • Chores

    • CI archives build artifacts and adds a dedicated container build job; adds scheduled container image cleanup and Dependabot updates for container tooling.
    • Adds multi-stage Docker build configurations for CI and local development.
  • Documentation

    • README updated with instructions for pulling and running the pre-built CLI container.

✏️ Tip: You can customize this high-level summary in your review settings.

@aj-stein aj-stein self-assigned this Dec 14, 2025
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Dec 14, 2025

📝 Walkthrough

Walkthrough

Adds multi-stage Dockerfiles, a reusable container build-and-publish workflow, a daily container cleanup workflow, and CI updates to upload the built CLI ZIP and invoke the reusable workflow to optionally build, tag, attest, and push multi-arch container images to GHCR. (≤50 words)

Changes

Cohort / File(s) Summary
CI workflow update
/.github/workflows/build.yml
Added step Upload build zip archive (artifact build_zip, path ./target/*oscal-cli.zip), added build-container job that calls the reusable workflow (input push: true), and adjusted job permissions for contents/packages/attestations/id-token.
Reusable container workflow
/.github/workflows/container.yml
New workflow_call workflow with push input: checks out repo, downloads build_zip, sets up QEMU/Buildx, logs into GHCR, generates metadata/tags/annotations, conditionally builds & pushes multi-arch images using Dockerfile.ci, and conditionally publishes attestations.
Container cleanup workflow
/.github/workflows/container-cleanup.yml
New scheduled/manual workflow to prune old container images using snok/container-retention-policy with a 7-day cutoff and keep 5 recent images; supports dry-run input.
CI container Dockerfile
Dockerfile.ci
New two-stage Dockerfile: builder stage unpacks target/*oscal-cli.zip into /opt/oscal-cli; runner stage copies artifacts, creates oscalcli user/group, sets ownership, verifies oscal-cli --version, and sets ENTRYPOINT to /opt/oscal-cli/bin/oscal-cli.
Local developer Dockerfile
Dockerfile.local
New multi-stage Dockerfile that builds the CLI with Maven in a builder stage (toolchains, package, extract oscal-cli.zip) and produces the same runner image layout and ENTRYPOINT.
Docs update
README.md
Added instructions for installing/running the pre-built CLI as a container (docker pull / docker run examples) placed after the shell export instructions.
Dependabot
/.github/dependabot.yml
Added Docker package-ecosystem entry for daily Dependabot updates targeting Dockerfile(s).

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant CI as build.yml (caller)
    participant Artifact as actions/upload-artifact
    participant Reusable as container.yml (called workflow)
    participant Checkout as actions/checkout
    participant QEMU as qemu/setup
    participant Buildx as docker/setup-buildx
    participant Docker as docker/build-push-action
    participant Registry as GHCR
    participant Attest as actions/attest-build-provenance

    CI->>Artifact: upload `build_zip` (./target/*oscal-cli.zip)
    CI->>Reusable: workflow_call(push: true)
    Reusable->>Checkout: checkout repo + submodules
    Reusable->>Artifact: download `build_zip` to ./target
    Reusable->>QEMU: setup QEMU (cross-arch)
    Reusable->>Buildx: setup Buildx (amd64, arm64)
    Reusable->>Docker: build image (Dockerfile.ci) with metadata/labels
    alt push == true
        Docker->>Registry: authenticate & push image (tags)
        Reusable->>Attest: publish attestation for image digest
        Attest->>Registry: store attestation
    else
        Docker-->>Reusable: build without push
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Poem

🐰 I zipped the bits and tucked them tight,
Builder hummed and QEMU took flight,
Tags rolled out and attestations chimed,
I hopped a push and watched the image climb,
Hop — the container danced into the night.

🚥 Pre-merge checks | ✅ 4 | ❌ 1
❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Linked Issues check ❓ Inconclusive The PR addresses most goals from issue #22: mechanism to download CLI with dependencies, installation without configuration, cleanup/versioning benefits, and verifiable automation-friendly verification. However, the acceptance criterion for updating website and README documentation is only partially completed. Verify completion of all website and README documentation updates required by issue #22 acceptance criteria and confirm CI-CD build process passes without errors.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: introducing OCI container image building for the CLI.
Out of Scope Changes check ✅ Passed All changes are directly related to building and publishing OCI container images for oscal-cli as specified in issue #22, with no unrelated modifications detected.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch 22-publish-docker-container

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (2)
Dockerfile (2)

13-13: Prefer COPY over ADD for source tree (and ensure a .dockerignore).
ADD . can introduce surprises (and typically bloats context/can hurt caching).

-ADD . /usr/local/src
+COPY . /usr/local/src

50-54: Recommended hardening: run as non-root in the runtime image.
Not strictly required, but a good default for published images.

 FROM ${RUNNER_IMAGE} AS runner
 COPY --from=builder /opt/oscal-cli-extended /opt/oscal-cli-extended
 WORKDIR /opt/oscal-cli-extended
+RUN addgroup -S oscal && adduser -S -G oscal oscal \
+  && chown -R oscal:oscal /opt/oscal-cli-extended
+USER oscal
 RUN /opt/oscal-cli-extended/bin/oscal-cli --version
 ENTRYPOINT [ "/opt/oscal-cli-extended/bin/oscal-cli" ]
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d675f21 and cb65569.

📒 Files selected for processing (1)
  • Dockerfile (1 hunks)
🧰 Additional context used
🪛 Hadolint (2.14.0)
Dockerfile

[error] 33-33: unexpected 'R'
expecting a new line followed by the next instruction

(DL1000)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Website
  • GitHub Check: Code

Comment thread Dockerfile Outdated
Comment thread Dockerfile Outdated
Comment thread Dockerfile Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cb65569 and 1029930.

📒 Files selected for processing (1)
  • .github/workflows/build.yml (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Website
  • GitHub Check: Code

Comment thread .github/workflows/build.yml Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (2)
Dockerfile (1)

42-47: Remove unnecessary pom.xml COPY or document its purpose.

Line 44 copies pom.xml to /tmp, but it is never referenced in the subsequent steps. The unzip operation on line 47 only needs the ZIP file. If pom.xml is not required, remove it to reduce layer size and clarify intent.

 # If using a prebuilt ZIP, require a deterministic filename in build context.
 # (CI can place it there before `docker build`.)
-COPY pom.xml /tmp/
 COPY oscal-cli-extended.zip /tmp/

If pom.xml serves a documented purpose (e.g., for build metadata or licensing), add a comment explaining it.

.github/workflows/build.yml (1)

119-122: Consider gating or removing verbose diagnostic commands.

Lines 119–122 run pwd, ls -lha, and find . unconditionally before every build. The find . command will produce extremely verbose output on a large repository and clutter CI logs without corresponding benefit for automated builds.

Remove these debug commands or gate them to manual/workflow_dispatch runs:

-    - run: |
-        pwd
-        ls -lha
-        find '.'
+    # Optional: uncomment for debugging
+    # - run: |
+    #     pwd
+    #     ls -lha
+    #     find . -type f -name '*.zip' | head -20

Or, conditionally run only on manual dispatch:

-    - run: |
+    - if: github.event_name == 'workflow_dispatch'
+      run: |
         pwd
         ls -lha
         find . -type f -name 'Dockerfile' -o -name 'oscal-cli-extended.zip'
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 91cd7b4 and 1e0eaf6.

📒 Files selected for processing (2)
  • .github/workflows/build.yml (3 hunks)
  • Dockerfile (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-12-13T15:28:58.145Z
Learnt from: david-waltermire
Repo: metaschema-framework/liboscal-java PR: 222
File: .github/workflows/build.yml:30-32
Timestamp: 2025-12-13T15:28:58.145Z
Learning: In the liboscal-java repository, nightly builds are managed independently using GitHub Actions schedule triggers (cron), not via repository_dispatch from metaschema-java. Each repo in the metaschema-framework manages its own nightly builds.

Applied to files:

  • .github/workflows/build.yml
🪛 Hadolint (2.14.0)
Dockerfile

[error] 33-33: unexpected 'R'
expecting a new line followed by the next instruction

(DL1000)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Code
🔇 Additional comments (4)
.github/workflows/build.yml (3)

43-50: Approve: Container registry environment variables and Docker metadata flags are well-configured.

The registry, image naming, and metadata environment variables are properly defined and follow Docker/OCI conventions. The DOCKER_METADATA_PR_HEAD_SHA and DOCKER_METADATA_ANNOTATIONS_LEVELS flags are correct for PR head SHA tracking and manifest/index-level annotations.


59-61: Approve: New permissions are now justified by container build/push/attestation steps.

The addition of packages: write, attestations: write, and id-token: write permissions is now justified by the container image build-push (line 160), login (line 131), and attestations steps (line 173). This follows the principle of least privilege correctly.


160-171: Verify: where does the prebuilt oscal-cli-extended.zip come from before Docker build?

The workflow sets USE_PREBUILT_ZIP=yes (line 46) and passes it to docker build (line 165), which tells the Dockerfile to use the prebuilt path. However, the Docker build-push step (line 160) has no preceding step that creates or copies oscal-cli-extended.zip into the build context.

The Maven build step (line 97) runs mvn ... deploy, but the output artifact placement is not visible. Verify that:

  1. The Maven build produces oscal-cli-extended.zip (or a file that is renamed to match).
  2. Before the Docker build-push step, the zip is copied to the repository root or build context so that line 45 of the Dockerfile (COPY oscal-cli-extended.zip /tmp/) succeeds.

Run this to trace artifact generation:

#!/bin/bash
# Check Maven pom.xml for the final artifact name and location
echo "=== Searching pom.xml for artifact assembly configuration ==="
fd pom.xml --exec grep -l "assembly\|finalName\|zip" {} \; 2>/dev/null | head -5

# Check if there's a step that moves the artifact before docker build
echo -e "\n=== Checking for Maven artifact handling in workflow ==="
sed -n '97,160p' .github/workflows/build.yml | grep -i "zip\|artifact\|target"

If the Maven build produces the zip but it's not copied to the root, add a step before the Docker build to copy it:

     - name: Perform CodeQL Analysis
       uses: github/codeql-action/analyze@1b168cd39490f61582a9beae412bb7057a6b2c4e
       with:
         upload: ${{ env.UPLOAD_CODEQL }}
+    - name: Prepare prebuilt ZIP for Docker build
+      run: |
+        if [ -f "target/oscal-cli-extended.zip" ]; then
+          cp target/oscal-cli-extended.zip .
+          echo "Copied prebuilt ZIP to build context"
+        else
+          echo "ERROR: oscal-cli-extended.zip not found in target/"
+          exit 1
+        fi
     - run: |
Dockerfile (1)

32-33: [Rewritten review comment]
[Classification tag]

Comment thread .github/workflows/build.yml Outdated
Comment thread .github/workflows/build.yml Outdated
Comment thread .github/workflows/build.yml Outdated
Comment thread Dockerfile Outdated
@aj-stein
Copy link
Copy Markdown
Contributor Author

Sigh, I will need to go back to the drawing board with this approach, it is more brittle than I thought. I will rubber duck debug with Claude and redesign it to be less brittle.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (2)
.github/workflows/build.yml (2)

122-126: CRITICAL: Fix YAML syntax error — uses: must precede with: in step definition.

Line 124 places with: before uses: on line 126, violating the GitHub Actions step schema. The correct order is useswith.

This is a persistent issue from previous reviews and will cause the workflow to fail at parse time.

  - name: Container image buildx setup for cross-arch builds
    id: image_setup_buildx
+   uses: docker/setup-buildx-action@988b5a0280414f521da01fcc63a27aeeb4b104db
    with:
      platforms: linux/amd64
-   uses: docker/setup-buildx-action@988b5a0280414f521da01fcc63a27aeeb4b104db

168-174: CRITICAL: Attestation step condition references non-existent main branch in triggers.

The conditional if: statement (line 168) checks github.ref == 'refs/heads/main', but the workflow only triggers on release/**, develop, and feature/** branches (lines 3–6, 10–12). The main branch never appears in the trigger configuration, so this condition is impossible to satisfy.

This is a persistent logic error from previous reviews. Fix by aligning with actual triggers:

-    - if: github.repository == env.HOME_REPO && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/develop' || startsWith(github.ref, 'refs/heads/feature'))
+    - if: github.repository == env.HOME_REPO && (github.ref == 'refs/heads/develop' || startsWith(github.ref, 'refs/heads/release/') || startsWith(github.ref, 'refs/heads/feature/'))

Alternatively, if attestations should only run on release branches:

-    - if: github.repository == env.HOME_REPO && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/develop' || startsWith(github.ref, 'refs/heads/feature'))
+    - if: github.repository == env.HOME_REPO && startsWith(github.ref, 'refs/heads/release/')

Clarify the intent: should attestations run on all branches (develop, feature, release), or only on release branches?

🧹 Nitpick comments (3)
Dockerfile.ci (2)

1-7: Builder stage uses Maven image solely for unpacking — consider a lighter alternative.

The builder stage pulls the entire Maven image (maven:3.9.9-eclipse-temurin-17-alpine ≈ 500+ MB) only to unzip a pre-built artifact. This adds unnecessary bloat to the build cache and layer history.

For a more efficient build, use a minimal Alpine image with unzip pre-installed, or a purpose-built unpacker base. The Maven image offers no benefit here since the CLI is already built by the Maven job (line 98 in build.yml).

Consider this lighter alternative:

-ARG BUILDER_IMAGE=maven:3.9.9-eclipse-temurin-17-alpine
+ARG BUILDER_IMAGE=alpine:latest
 ARG RUNNER_IMAGE=eclipse-temurin:17-alpine
 
 FROM ${BUILDER_IMAGE} AS builder
+RUN apk add --no-cache unzip
 COPY ./target/*oscal-cli.zip /tmp/oscal-cli.zip
 WORKDIR /tmp
 RUN unzip /tmp/oscal-cli.zip -d /opt/oscal-cli

This reduces the builder image size and improves clarity of intent.


12-14: Verify binary integrity with explicit exit code check.

The verification step (line 13) runs oscal-cli --version but doesn't explicitly verify the exit code. If the binary is corrupted or missing, Docker build will not fail.

Consider adding explicit failure on non-zero exit:

-RUN chmod +x /opt/oscal-cli/bin/oscal-cli && \
-    /opt/oscal-cli/bin/oscal-cli --version
+RUN chmod +x /opt/oscal-cli/bin/oscal-cli && \
+    /opt/oscal-cli/bin/oscal-cli --version || exit 1

Alternatively, structure as separate commands to aid debugging:

-RUN chmod +x /opt/oscal-cli/bin/oscal-cli && \
-    /opt/oscal-cli/bin/oscal-cli --version
+RUN chmod +x /opt/oscal-cli/bin/oscal-cli
+RUN /opt/oscal-cli/bin/oscal-cli --version

The second approach makes failures easier to diagnose in build logs.

.github/workflows/build.yml (1)

43-50: Remove unused environment variable USE_PREBUILT_ZIP.

Line 46 defines USE_PREBUILT_ZIP: yes, but this variable is never referenced in the workflow. The Dockerfile.ci already assumes a pre-built ZIP at ./target/*oscal-cli.zip (copied at build.yml line 5), so no conditional logic needs this flag.

Per the principle of least surprise, remove unused variables:

  REGISTRY: ghcr.io
  HOME_REPO:  metaschema-framework/oscal-cli
  IMAGE_NAME: metaschema-framework/oscal-cli
- USE_PREBUILT_ZIP: yes
  # Docs: github.com/docker/metadata-action/?tab=readme-ov-file#typesha
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5a51dc9 and 2029999.

📒 Files selected for processing (2)
  • .github/workflows/build.yml (3 hunks)
  • Dockerfile.ci (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-12-13T15:28:58.145Z
Learnt from: david-waltermire
Repo: metaschema-framework/liboscal-java PR: 222
File: .github/workflows/build.yml:30-32
Timestamp: 2025-12-13T15:28:58.145Z
Learning: In the liboscal-java repository, nightly builds are managed independently using GitHub Actions schedule triggers (cron), not via repository_dispatch from metaschema-java. Each repo in the metaschema-framework manages its own nightly builds.

Applied to files:

  • .github/workflows/build.yml
🔇 Additional comments (2)
.github/workflows/build.yml (2)

127-133: Container registry login is correctly configured.

The login step properly uses the standard GITHUB_TOKEN secret for ghcr.io authentication and references environment variables for dynamic configuration.


134-156: Metadata and annotations configuration is comprehensive and follows OCI standards.

The docker/metadata-action properly constructs image tags (SHA, branch, tag, PR refs) with latest flavor, and all OCI annotations are present (authors, docs, source, vendor, title, description, licenses). The use of environment variables for registry and image name enables dynamic configuration.

Comment thread .github/workflows/build.yml Outdated
Comment thread .github/workflows/build.yml Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
Dockerfile.local (1)

27-34: Consider consolidating RUN instructions to reduce image layers.

Multiple consecutive RUN instructions (apk add, mvn, find, mkdir, unzip, chmod) can be combined into fewer layers to reduce image size and improve cacheability. This is a recommended refactor for production images.

Apply this diff to consolidate build steps:

-RUN apk add --no-cache git unzip
-RUN mvn -B -e -Prelease package
-RUN find /usr/local/src/target \
-    -iname '*oscal-cli.zip' \
-    -exec cp {} /tmp/oscal-cli.zip \;
-RUN mkdir -p /opt/oscal-cli
-RUN unzip /tmp/oscal-cli.zip -d /opt/oscal-cli
-RUN chmod +x /opt/oscal-cli/bin/oscal-cli
+RUN apk add --no-cache git unzip && \
+    mvn -B -e -Prelease package && \
+    mkdir -p /opt/oscal-cli && \
+    unzip /tmp/oscal-cli.zip -d /opt/oscal-cli && \
+    chmod +x /opt/oscal-cli/bin/oscal-cli

Note: The find command should be handled separately (see previous comment about error handling) before consolidating.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2029999 and 8586521.

📒 Files selected for processing (2)
  • Dockerfile.ci (1 hunks)
  • Dockerfile.local (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • Dockerfile.ci
🧰 Additional context used
🪛 Hadolint (2.14.0)
Dockerfile.local

[error] 27-27: unexpected 'R'
expecting a new line followed by the next instruction

(DL1000)

🔇 Additional comments (2)
Dockerfile.local (2)

29-31: Add error handling to the find command.

The find command silently succeeds even if oscal-cli.zip is not found. If the file location changes or doesn't exist, the build continues and fails later at the unzip step (line 33) with a cryptic error, making debugging harder.

Apply this diff to fail fast if the file isn't found:

 RUN find /usr/local/src/target \
     -iname '*oscal-cli.zip' \
-    -exec cp {} /tmp/oscal-cli.zip \;
+    -exec cp {} /tmp/oscal-cli.zip \; \
+    || (echo "ERROR: oscal-cli.zip not found in target directory" && exit 1)

Alternatively, use a more explicit approach:

-RUN find /usr/local/src/target \
-    -iname '*oscal-cli.zip' \
-    -exec cp {} /tmp/oscal-cli.zip \;
+RUN set -e && \
+    ZIPFILE=$(find /usr/local/src/target -iname '*oscal-cli.zip' | head -1) && \
+    [ -n "$ZIPFILE" ] || (echo "ERROR: oscal-cli.zip not found" && exit 1) && \
+    cp "$ZIPFILE" /tmp/oscal-cli.zip

28-28: Clarify test execution during Maven build.

The Maven command mvn -B -e -Prelease package will execute the project's test suite (unless configured otherwise in pom.xml). Depending on the test count, this could significantly extend build time. Consider whether tests should be skipped during container builds using -DskipTests, or if test execution is intentional for validation.

Comment thread Dockerfile.local Outdated
@aj-stein aj-stein force-pushed the 22-publish-docker-container branch from 8586521 to 8d7c76d Compare December 15, 2025 05:18
@aj-stein aj-stein marked this pull request as ready for review December 15, 2025 05:27
@aj-stein
Copy link
Copy Markdown
Contributor Author

It's only been like a year @david-waltermire, but I did it since we're getting the band back together so to speak. Thanks for waiting for such a speedy PR on my part. 😆

@david-waltermire
Copy link
Copy Markdown
Contributor

The PR adds container building to the "build" workflow, which runs on release/**, develop, and feature/** changes.

Since this adds a good deal of complexity to the build workflow, should we move the container build into a seperate, parallel workflow?

@david-waltermire david-waltermire linked an issue Dec 18, 2025 that may be closed by this pull request
7 tasks
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
.github/workflows/container.yml (1)

68-80: Consider adding SBOM generation and vulnerability scanning.

While not blocking, consider enhancing the security posture by:

  1. Adding SBOM (Software Bill of Materials) generation using docker/build-push-action's built-in support via sbom: true.
  2. Adding container vulnerability scanning (e.g., using Trivy or similar tools) before publishing.

These additions would align with supply chain security best practices mentioned in the PR objectives regarding "verifiable, automation-friendly way to securely check the CLI."

🔎 Example SBOM addition
     platforms: linux/amd64,linux/arm64/v8
     cache-from: type=gha
     cache-to: type=gha,mode=max
+    sbom: true
+    provenance: true
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 317f9a2 and 645f8f5.

📒 Files selected for processing (2)
  • .github/workflows/build.yml (1 hunks)
  • .github/workflows/container.yml (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • .github/workflows/build.yml
🔇 Additional comments (9)
.github/workflows/container.yml (9)

1-9: LGTM! Well-structured reusable workflow.

The workflow_call trigger with a boolean push input provides good control over registry publishing behavior, with a safe default of false.


10-16: LGTM! Environment variables are well-configured.

The use of DOCKER_METADATA_PR_HEAD_SHA: true ensures reproducible builds for PRs, and annotation levels are appropriate for multi-arch images.


18-26: LGTM! Permissions follow least-privilege principle.

The job configuration uses ubuntu-24.04 and grants only the necessary permissions for building, publishing, and attesting container images.


31-33: LGTM! QEMU setup enables cross-architecture builds.

Standard configuration for multi-arch container builds with properly pinned action.


39-45: LGTM! Standard GHCR authentication.

The login step correctly uses github.actor and GITHUB_TOKEN for authentication to GitHub Container Registry.


46-67: LGTM! Comprehensive metadata and tagging strategy.

The metadata generation includes appropriate tags (SHA, branch, tag, PR) and follows OCI annotation standards with comprehensive project information.


27-30: Good checkout configuration with important caveat

The pinned hash, submodules: recursive, and filter: tree:0 are all appropriate and well-supported. However, verify that Git LFS is not enabled (lfs: true) in this step, as it is incompatible with treeless clones.


34-38: Fix YAML syntax error: uses must come before with.

The GitHub Actions syntax requires the uses keyword to appear before the with keyword. The current order will cause a workflow syntax error.

🔎 Proposed fix
 - name: Container image buildx setup for cross-arch builds
   id: image_setup_buildx
+  uses: docker/setup-buildx-action@988b5a0280414f521da01fcc63a27aeeb4b104db
   with:
     platforms: linux/amd64,linux/arm64/v8
-  uses: docker/setup-buildx-action@988b5a0280414f521da01fcc63a27aeeb4b104db

Likely an incorrect or invalid review comment.


81-87: Set push-to-registry: true to align attestation storage with image deployment.

When inputs.push is true, the container image is pushed to the registry, but the attestation defaults to false, storing it only in GitHub's Attestations API rather than the OCI registry. For consistency and to enable registry-level attestation verification (including Kubernetes admission controllers), attestations should be stored alongside the image.

Comment thread .github/workflows/container.yml
@aj-stein
Copy link
Copy Markdown
Contributor Author

aj-stein commented Dec 20, 2025

@david-waltermire this should be ready for re-review. I split out the build and container build workflows like you asked. I will add an issue about building scheduled registry pruners to address feedback during our PR review convo.

Nevermind, I guess I take that back. lol

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
Dockerfile.local (1)

9-9: Consider using COPY instead of ADD and adding .dockerignore.

ADD has implicit tar extraction and URL fetching capabilities. For copying local directories, COPY is more explicit and preferred. Additionally, consider creating a .dockerignore file to exclude unnecessary files (e.g., .git, target, node_modules) from the build context.

🔎 Suggested improvement
-ADD . /usr/local/src
+COPY . /usr/local/src

And create a .dockerignore file at the repository root:

.git
target
*.class
*.jar
.DS_Store
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d6526e8 and 8e2d6a1.

📒 Files selected for processing (2)
  • Dockerfile.ci (1 hunks)
  • Dockerfile.local (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • Dockerfile.ci
🧰 Additional context used
🪛 Hadolint (2.14.0)
Dockerfile.local

[error] 28-28: unexpected 'R'
expecting a new line followed by the next instruction

(DL1000)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Code
🔇 Additional comments (3)
Dockerfile.local (3)

1-4: LGTM: BuildKit directive and base image setup are correct.

The BuildKit syntax directive is properly placed, and the base images are appropriately pinned to specific versions.


37-41: LGTM: Runner stage is clean and well-structured.

The runner stage correctly:

  • Uses a minimal base image
  • Copies only the necessary artifacts from the builder
  • Validates the CLI binary with a version check
  • Sets an appropriate entrypoint for container usage

29-35: Code implementation is correct as-is.

Dockerfile.local properly builds the project from source using Maven (line 29), making it self-contained for local development. Dockerfile.ci, as expected, receives pre-built artifacts from the CI pipeline and simply extracts and installs them. This separation of concerns is the intended design—each Dockerfile serves its purpose correctly.

Comment thread Dockerfile.local Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (2)
Dockerfile.ci (2)

6-6: Remove unnecessary git package.

The git package is installed but never used in the builder stage. The builder only unpacks the ZIP artifact and doesn't perform any Git operations. Removing it will reduce the build time and intermediate layer size.

🔎 Proposed fix (after fixing the package manager issue)
-RUN apt-get update && apt-get install -y --no-install-recommends git unzip && rm -rf /var/lib/apt/lists/*
+RUN apt-get update && apt-get install -y --no-install-recommends unzip && rm -rf /var/lib/apt/lists/*

2-3: Update base image versions to latest stable releases.

Replace maven:3.9.9-eclipse-temurin-17 with maven:3.9.12-eclipse-temurin-17 (or at minimum maven:3.9.10-eclipse-temurin-17) and use an explicit Eclipse Temurin 17 version tag such as eclipse-temurin:17.0.17 instead of the untagged eclipse-temurin:17 to ensure consistent, secure builds with known CVE patches applied.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8e2d6a1 and 2edad5f.

📒 Files selected for processing (1)
  • Dockerfile.ci (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Code

Comment thread Dockerfile.ci Outdated
Comment thread Dockerfile.ci
Comment thread Dockerfile.ci
@aj-stein
Copy link
Copy Markdown
Contributor Author

Ok, now it is ready for review @david-waltermire.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
Dockerfile.local (1)

11-11: Clean up apt cache to reduce image size.

The apt-get install command should include --no-install-recommends and remove cached package lists to follow Docker best practices and minimize the builder layer size.

🔎 Proposed fix
-RUN apt-get update && apt-get install -y unzip
+RUN apt-get update && apt-get install -y --no-install-recommends unzip \
+    && rm -rf /var/lib/apt/lists/*
.github/workflows/container.yml (1)

90-91: Inconsistent template spacing.

Line 91 is missing a space before the closing braces in ${{ env.IMAGE_NAME}}. While this may still parse correctly, it's inconsistent with the spacing used elsewhere (e.g., line 90: ${{ env.REGISTRY }}).

🔎 Proposed fix
-        subject-name: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME}}
+        subject-name: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2edad5f and ca95dd2.

📒 Files selected for processing (3)
  • .github/workflows/container.yml (1 hunks)
  • Dockerfile.ci (1 hunks)
  • Dockerfile.local (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • Dockerfile.ci
🧰 Additional context used
🪛 Hadolint (2.14.0)
Dockerfile.local

[error] 30-30: unexpected 'R'
expecting a new line followed by the next instruction

(DL1000)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Code
🔇 Additional comments (3)
Dockerfile.local (2)

30-30: Hadolint DL1000 is a false positive.

The static analysis tool reports an "unexpected 'R'" error on this line, but this is a false positive. The heredoc (lines 12-29) correctly terminates with EOF on line 29, and line 30 is a valid RUN instruction. This error likely stems from the linter's incomplete understanding of BuildKit heredoc syntax.


38-46: LGTM: Security and verification practices are solid.

The runner stage implements good practices by creating a dedicated non-root user (oscalcli), restricting privileges, and verifying the installation with a version check before setting the entrypoint.

.github/workflows/container.yml (1)

51-72: LGTM: Comprehensive OCI metadata annotations.

The metadata configuration provides thorough OCI-compliant annotations including maintainers, documentation, source, vendor, and licensing information. The tagging strategy (SHA, branch, tag, PR) covers the necessary deployment scenarios.

Comment thread .github/workflows/container.yml Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
Dockerfile.local (1)

11-11: Consider cleaning up apt lists to reduce builder image size.

While this is a builder stage and the runner stage won't inherit these layers, cleaning up apt cache reduces local storage and registry cache size.

🔎 Suggested cleanup
-RUN apt-get update && apt-get install -y unzip
+RUN apt-get update && apt-get install -y --no-install-recommends unzip && rm -rf /var/lib/apt/lists/*
Dockerfile.ci (1)

6-6: Consider cleaning up apt lists to reduce builder image size.

Similar to Dockerfile.local, adding cleanup reduces cache and storage footprint.

🔎 Suggested cleanup
-RUN apt-get update && apt-get install -y unzip
+RUN apt-get update && apt-get install -y --no-install-recommends unzip && rm -rf /var/lib/apt/lists/*
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ca95dd2 and ab51a8a.

📒 Files selected for processing (3)
  • .github/workflows/container.yml (1 hunks)
  • Dockerfile.ci (1 hunks)
  • Dockerfile.local (1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-12-13T15:29:03.112Z
Learnt from: david-waltermire
Repo: metaschema-framework/liboscal-java PR: 222
File: .github/workflows/build.yml:30-32
Timestamp: 2025-12-13T15:29:03.112Z
Learning: In the liboscal-java repository, nightly builds are managed independently using GitHub Actions schedule triggers (cron), not via repository_dispatch from metaschema-java. Each repo in the metaschema-framework manages its own nightly builds.

Applied to files:

  • Dockerfile.ci
  • .github/workflows/container.yml
📚 Learning: 2025-12-17T13:27:43.679Z
Learnt from: CR
Repo: metaschema-framework/metaschema-java PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-17T13:27:43.679Z
Learning: All changes require PR review with CODEOWNERS enforcement

Applied to files:

  • Dockerfile.ci
🪛 Hadolint (2.14.0)
Dockerfile.local

[error] 30-30: unexpected 'R'
expecting a new line followed by the next instruction

(DL1000)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Code
🔇 Additional comments (6)
Dockerfile.local (1)

38-48: LGTM! Excellent security posture.

The runner stage correctly implements security best practices: non-root user with restricted shell, proper ownership of application directory, separate working directory for user mounts, and verification that runs as the non-root user before the container starts.

Dockerfile.ci (1)

12-21: LGTM! Consistent security implementation.

The runner stage mirrors Dockerfile.local with the same secure non-root setup. The past review feedback on non-root user, correct package manager, and metadata has been properly addressed.

.github/workflows/container.yml (4)

1-16: LGTM! Well-structured reusable workflow.

The workflow_call pattern with the push input provides good flexibility for CI/CD. Environment variables are properly scoped, and the Docker metadata configuration flags (PR_HEAD_SHA, ANNOTATIONS_LEVELS) follow current best practices.


17-43: LGTM! Proper permissions and multi-arch setup.

The job configuration follows security best practices with appropriate least-privilege permissions. The multi-architecture build setup (QEMU + Buildx for amd64 and arm64/v8) is correctly configured, and the artifact download step properly retrieves the pre-built CLI zip.


51-72: LGTM! Comprehensive container metadata.

The metadata configuration is thorough and follows best practices:

  • SHA-based tags for immutability
  • Ref-based tags for readability
  • latest tag restricted to main branch (per commit message intent)
  • Complete OCI annotations with proper maintainer info, documentation URLs, and CC0-1.0 license

86-92: Verify attestation publishing strategy.

Line 92 sets push-to-registry: false for build provenance attestations. This means attestations are generated but not published to the registry alongside the image.

If you want users to be able to verify the image provenance (e.g., via docker buildx imagetools inspect or cosign verify-attestation), consider setting push-to-registry: true to publish attestations to GHCR alongside the image.

Current behavior:

  • Attestations are generated and stored in GitHub Actions artifacts/logs
  • Users cannot verify provenance directly from the registry

Alternative (if desired):

     push-to-registry: false
+    push-to-registry: true

This would enable end-users to verify supply chain provenance for published images.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
README.md (1)

73-81: Consider documenting available version tags and multi-architecture support.

The documentation shows only the latest tag but doesn't explain the tagging strategy (stable releases, pre-releases, version-specific tags, etc.). Since the AI summary indicates multi-architecture images are built, it would be helpful to mention that users can run the container on different architectures without additional steps.

🔎 Example enhancement

You might consider expanding the documentation like this (optional):

 ### Installing pre-built package as a container image 
 
-You can also download pre-release and official release versions of the CLI as an OCI-conformant image with `docker`, `podman`, or other tools.
+You can also download pre-release and official release versions of the CLI as an OCI-conformant image with `docker`, `podman`, or other tools. Images are published with version tags (e.g., `v1.0.0`) and the `latest` tag for the most recent release. Multi-architecture images are available and automatically selected for your platform.
 
 \`\`\`sh
 docker pull ghcr.io/metaschema-framework/oscal-cli:latest
-docker run -it ghcr.io/metaschema-framework/oscal-cli:latest --version
+docker run ghcr.io/metaschema-framework/oscal-cli:latest --version
 \`\`\`
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ac14670 and 70a80c6.

📒 Files selected for processing (1)
  • README.md
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Code

Comment thread README.md Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In @.github/workflows/container-cleanup.yml:
- Around line 22-30: Add a permissions block to the workflow with "packages:
write" and ensure the token used can manage GHCR packages: either grant the
org-level package "Manage Actions access" to this repository (so GITHUB_TOKEN
can delete the images) or replace token: ${{ secrets.GITHUB_TOKEN }} with a
repository secret holding a PAT that has write:packages and delete:packages
(e.g. token: ${{ secrets.PACKAGES_TOKEN }}). Ensure the new permissions block
(permissions: packages: write) is added at workflow root and update the token
reference in the container-cleanup job if using a PAT.
🧹 Nitpick comments (2)
Dockerfile.local (1)

31-36: Consider adding error handling for the find command.

The find ... -exec cp will exit successfully even if no matching file is found, potentially causing confusing failures later when the zip doesn't exist.

🔎 Suggested improvement
-RUN find /usr/local/src/target \
-    -iname '*oscal-cli.zip' \
-    -exec cp {} /tmp/oscal-cli.zip \;
+RUN find /usr/local/src/target \
+    -iname '*oscal-cli.zip' \
+    -exec cp {} /tmp/oscal-cli.zip \; && \
+    test -f /tmp/oscal-cli.zip
.github/workflows/container-cleanup.yml (1)

25-27: Consider documenting the retention policy rationale.

The retention settings (7-day cutoff, keep 5 most recent, exclude latest and versioned tags) are reasonable, but adding inline comments would help future maintainers understand the tradeoff between storage costs and image availability.

📝 Example with documentation comments
       with:
         account: metaschema-framework
         image-names: oscal-cli
+        # Keep images from the last week to support recent development
         cut-off: 7d
+        # Retain at least 5 images even if older than 7d for rollback capability
         keep-n-most-recent: 5
+        # Preserve release versions (v*) and the latest tag indefinitely
         image-tags: "!latest !v*"
         tag-selection: both
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 70a80c6 and d04cb42.

📒 Files selected for processing (6)
  • .github/dependabot.yml
  • .github/workflows/build.yml
  • .github/workflows/container-cleanup.yml
  • .github/workflows/container.yml
  • Dockerfile.ci
  • Dockerfile.local
🚧 Files skipped from review as they are similar to previous changes (3)
  • .github/workflows/container.yml
  • Dockerfile.ci
  • .github/workflows/build.yml
🧰 Additional context used
🪛 Hadolint (2.14.0)
Dockerfile.local

[error] 30-30: unexpected 'R'
expecting a new line followed by the next instruction

(DL1000)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Code
🔇 Additional comments (5)
.github/dependabot.yml (1)

16-21: LGTM!

The Docker ecosystem configuration follows the established pattern of the existing entries (daily interval, develop target branch) and correctly points to the repository root where the Dockerfiles reside.

Dockerfile.local (3)

1-10: LGTM!

The BuildKit syntax directive enables heredoc support, and the parameterized base images via ARGs provide good flexibility for local customization.


11-29: Correct package manager and valid heredoc syntax.

The apt-get usage is correct for the Debian-based Maven image. The Hadolint DL1000 error on line 30 is a false positive—Hadolint doesn't fully support BuildKit heredoc syntax, which is properly enabled by the # syntax=docker/dockerfile:1 directive.

Optionally, you could add --no-install-recommends and cleanup the apt cache (rm -rf /var/lib/apt/lists/*) on line 11, but since this is a builder stage that isn't persisted, the impact is minimal.


38-49: Good security practices in the runner stage.

The non-root user setup with proper group/user creation, ownership assignment, and the build-time version verification are solid practices. The /app working directory provides a clean mount point for users.

.github/workflows/container-cleanup.yml (1)

21-21: The action SHA is valid and correctly pinned to v3.0.1.

The pinned commit SHA 3b0972b2276b171b212f8c4efbca59ebba26eceb is verified as the official v3.0.1 release from September 23, 2025, and represents a sound security practice. The version remains reasonably current with no newer releases available; recent commits in the repository contain only dependency updates.

Comment thread .github/workflows/container-cleanup.yml
Copy link
Copy Markdown
Contributor Author

@aj-stein aj-stein left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't directly approve as an author, @david-waltermire. It looks good to me, please merge.

I was unable to quickly see some context on mobile when I looked earlier. I look forward to using the container work, so 🚀 🚢 !

aj-stein and others added 19 commits January 7, 2026 20:35
- Update Docker action versions to latest
- Add chown for /app directory in Dockerfiles
- Add container-cleanup.yml workflow for image pruning
- Add docker ecosystem to dependabot.yml
- Skip container build for fork PRs (no registry access)
The -it flags allocate an interactive terminal which is unnecessary
for the --version command that executes and exits immediately.
@aj-stein aj-stein force-pushed the 22-publish-docker-container branch from 0421b0a to 50e6ede Compare January 8, 2026 01:35
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In @.github/workflows/container.yml:
- Around line 62-63: The current condition setting latest (the line starting
with latest= and currently comparing github.ref to refs/heads/main) is
unreachable because builds never run on main; update that expression to match
your intended tagging strategy—e.g., set it to check refs/heads/develop for a
rolling latest from develop, use startsWith(github.ref, 'refs/heads/release/')
for release branches, use startsWith(github.ref, 'refs/tags/v') for version
tags, or alternatively add main to the build triggers in build.yml; locate the
existing latest=... entry in .github/workflows/container.yml and replace the
condition accordingly.
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0421b0a and 50e6ede.

📒 Files selected for processing (7)
  • .github/dependabot.yml
  • .github/workflows/build.yml
  • .github/workflows/container-cleanup.yml
  • .github/workflows/container.yml
  • Dockerfile.ci
  • Dockerfile.local
  • README.md
🚧 Files skipped from review as they are similar to previous changes (2)
  • .github/workflows/container-cleanup.yml
  • .github/dependabot.yml
🧰 Additional context used
🧠 Learnings (8)
📚 Learning: 2026-01-05T16:33:59.206Z
Learnt from: david-waltermire
Repo: metaschema-framework/metaschema-java PR: 0
File: :0-0
Timestamp: 2026-01-05T16:33:59.206Z
Learning: In metaschema-framework/metaschema-java, the core/metaschema submodule is owned by this project's maintainers; updates to it are coordinated within this repository, so no follow-up issues should be opened in external repositories for submodule changes.

Applied to files:

  • .github/workflows/container.yml
  • Dockerfile.ci
📚 Learning: 2025-12-13T15:29:03.112Z
Learnt from: david-waltermire
Repo: metaschema-framework/liboscal-java PR: 222
File: .github/workflows/build.yml:30-32
Timestamp: 2025-12-13T15:29:03.112Z
Learning: In the liboscal-java repository, nightly builds are managed independently using GitHub Actions schedule triggers (cron), not via repository_dispatch from metaschema-java. Each repo in the metaschema-framework manages its own nightly builds.

Applied to files:

  • .github/workflows/container.yml
  • Dockerfile.ci
📚 Learning: 2026-01-07T04:40:01.060Z
Learnt from: david-waltermire
Repo: metaschema-framework/metaschema-java PR: 623
File: .github/workflows/build.yml:8-14
Timestamp: 2026-01-07T04:40:01.060Z
Learning: In the metaschema-java repository, the build workflow intentionally includes `main` in the `pull_request.branches` trigger even though CONTRIBUTING.md requires PRs to target `develop`. This is a defensive design to provide feedback if someone accidentally targets main, rather than silent failure. The UPLOAD_SCAN_SARIF logic also needs main for security scan results.

Applied to files:

  • .github/workflows/build.yml
📚 Learning: 2026-01-07T17:36:32.578Z
Learnt from: david-waltermire
Repo: metaschema-framework/oscal-cli PR: 0
File: :0-0
Timestamp: 2026-01-07T17:36:32.578Z
Learning: In metaschema-framework repositories, nightly builds skip security scans (CodeQL and Trivy) because: (1) security scans run on every PR and push to main/develop/release branches providing continuous coverage, (2) GitHub Dependabot provides real-time alerts for dependency vulnerabilities between PRs, and (3) nightly builds focus on catching integration issues with upstream dependencies rather than security vulnerabilities. This is an intentional optimization that maintains security posture.

Applied to files:

  • .github/workflows/build.yml
📚 Learning: 2025-12-17T13:27:43.679Z
Learnt from: CR
Repo: metaschema-framework/metaschema-java PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-17T13:27:43.679Z
Learning: All PRs require passing CI checks before merge

Applied to files:

  • .github/workflows/build.yml
📚 Learning: 2025-12-17T13:27:43.679Z
Learnt from: CR
Repo: metaschema-framework/metaschema-java PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-17T13:27:43.679Z
Learning: All changes require PR review with CODEOWNERS enforcement

Applied to files:

  • .github/workflows/build.yml
  • Dockerfile.ci
📚 Learning: 2025-12-17T13:27:43.679Z
Learnt from: CR
Repo: metaschema-framework/metaschema-java PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-17T13:27:43.679Z
Learning: All PRs must be created from a personal fork and must target the develop branch (BLOCKING - required by CONTRIBUTING.md)

Applied to files:

  • .github/workflows/build.yml
📚 Learning: 2025-12-17T13:27:43.679Z
Learnt from: CR
Repo: metaschema-framework/metaschema-java PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-17T13:27:43.679Z
Learning: Squash non-relevant commits before submitting PR (BLOCKING)

Applied to files:

  • .github/workflows/build.yml
🪛 Hadolint (2.14.0)
Dockerfile.local

[error] 30-30: unexpected 'R'
expecting a new line followed by the next instruction

(DL1000)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Code
🔇 Additional comments (7)
README.md (1)

73-81: LGTM! Clear container installation documentation.

The new documentation section provides straightforward instructions for using the CLI as a container image. The commands follow Docker best practices, and the placement before the "Running" section is logical.

Dockerfile.local (1)

1-49: Clarify the purpose and usage of Dockerfile.local.

This Dockerfile appears technically sound (all past issues have been addressed), but based on your earlier comment "No need for a build container we are not saving it," it's unclear whether this file is actually needed.

The PR uses Dockerfile.ci for the CI/CD pipeline, so the purpose of Dockerfile.local is ambiguous. If it's intended for local development testing, please document this in:

  • A comment at the top of the file
  • The README's "Building" section

If it's not needed, consider removing it to reduce maintenance burden.

Note: The Hadolint DL1000 error on line 30 is a false positive—the RUN command is valid, and Hadolint is misinterpreting the heredoc syntax.

.github/workflows/build.yml (2)

108-112: LGTM! Artifact upload correctly configured.

The build zip upload step properly packages the Maven build output for consumption by the container workflow. The artifact name build_zip matches the download step in container.yml (line 34).


113-125: LGTM! Container job properly configured with security controls.

The build-container job correctly:

  • Skips for fork PRs (preventing unauthorized registry access)
  • Declares appropriate permissions for container operations
  • Depends on build-code to ensure artifacts are available
  • Uses the reusable container workflow with push enabled
Dockerfile.ci (1)

1-22: LGTM! Well-structured multi-stage build with security best practices.

The Dockerfile.ci correctly implements:

  • BuildKit syntax for advanced features
  • Minimal builder stage for extracting the pre-built CLI
  • Security-hardened runner stage with non-root user (oscalcli)
  • Runtime verification step before finalizing the image

All previous review feedback has been incorporated. The image is production-ready.

.github/workflows/container.yml (2)

73-85: LGTM! Build and push configuration is correct.

The build-push step properly:

  • Guards registry push with inputs.push conditional
  • Builds for both linux/amd64 and linux/arm64/v8 (matching buildx configuration on line 43)
  • References the correct Dockerfile (Dockerfile.ci)
  • Uses metadata outputs for tags and labels
  • Leverages GitHub Actions cache for faster subsequent builds

86-92: LGTM! Attestation configuration follows best practices.

The build provenance attestation correctly:

  • Runs only when images are pushed (conditional on inputs.push)
  • References the built image via subject-name and digest
  • Pushes attestation to the registry for verification

This provides supply chain security via SLSA provenance.

Comment thread .github/workflows/container.yml
@david-waltermire david-waltermire merged commit 5d90019 into develop Jan 8, 2026
4 checks passed
@david-waltermire david-waltermire deleted the 22-publish-docker-container branch January 8, 2026 01:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Publish docker container

2 participants