Repository navigation
chore(ci): build and publish the Stacklet cubejs/cube image - #2
Conversation
There was a problem hiding this comment.
2 issues found across 9 files
Confidence score: 2/5
- In
.github/workflows/stacklet-image.yml, same-repository pull requests run the PR’s workflow revision, which can request OIDC credentials for the ECR publishing role. Keep AWS-authenticated publishing out of pull request runs. - In
scripts/stacklet-overlay-guard.sh, root-level files such asrust/.gitignorebypass the dropped-path check, so changes that do not reach the image can pass the guard. Match both root-level and nested Rust paths.
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name=".github/workflows/stacklet-image.yml">
<violation number="1" location=".github/workflows/stacklet-image.yml:84">
P1: Custom agent: **Flag Security Vulnerabilities**
Same-repository `pull_request` runs execute the PR’s workflow revision, and this job can request OIDC credentials for the ECR publishing role. Keep AWS-authenticated publication to trusted push runs or require an explicit trusted approval gate for PRs.</violation>
</file>
<file name="scripts/stacklet-overlay-guard.sh">
<violation number="1" location="scripts/stacklet-overlay-guard.sh:74">
P2: Root-level files under `rust/` bypass the dropped-path check, so a change to `rust/.gitignore` is silently accepted despite not reaching the image. Match both `rust/<file>` and nested Rust paths.</violation>
</file>
Architecture diagram
sequenceDiagram
participant Dev as Developer
participant Git as GitHub Repo
participant WF as Stacklet Image Workflow
participant Node as Node.js Build Env
participant Docker as Docker Build/Runtime
participant ECR as AWS ECR Registry
participant AWS as AWS OIDC Auth
Note over Dev,Git: PR or push triggers workflow
Dev->>Git: Push to stacklet/integration or release/* branch
Git->>WF: Trigger Stacklet Image workflow
Note over WF: Job 1: Overlay Guard
WF->>Node: Run stacklet-overlay-guard.sh
Node->>Git: Fetch upstream release tag(s)
Git-->>Node: Upstream tag refs
Node->>Node: Compare changed files vs upstream
alt Unreachable source changes detected
Node-->>WF: FAIL - changes not in image
else Overlay passed
Node-->>WF: Overlay OK
end
Note over WF: Job 2: Resolve Image Tag
WF->>AWS: Assume publish role (OIDC)
AWS-->>WF: Temporary credentials
WF->>Node: Resolve tag from package.json + commit sha
Node-->>WF: Image tag (e.g. v1.2.3-stacklet-pr42-abc123def)
WF->>ECR: Check if tag already exists
alt Tag exists
ECR-->>WF: Exists - skip build
else Tag missing
ECR-->>WF: Not found - proceed to build
end
Note over WF: Job 3: Build (no cloud creds)
WF->>Node: yarn install + tsc compile (PATCHED_PACKAGES list)
Node->>Node: Copy root yarn.lock into build context
Node->>Docker: docker build --platform linux/amd64
Docker->>Node: Install npm packages + overlay compiled dist
alt Patch overlay fails
Node-->>WF: FAIL - package not installed or copy error
else Build succeeds
Docker-->>Node: Image built
Node->>Docker: Run container, probe /livez
alt Health check fails
Docker-->>WF: FAIL - smoke test error
else Health check passes
Docker-->>Node: Save image artifact
end
end
Note over WF: Job 4: Publish
WF->>AWS: Assume publish role (OIDC)
AWS-->>WF: Temporary credentials
WF->>ECR: Push image to ECR repository
ECR-->>WF: Image pushed
WF->>ECR: Validate single-platform manifest
ECR-->>WF: Manifest verified
WF-->>Git: Workflow complete
Note over Dev,Git: Final state: image published and tagged in ECR
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
| if: vars.PUBLISH_ROLE_ARN != '' | ||
| permissions: | ||
| contents: read | ||
| id-token: write |
There was a problem hiding this comment.
P1: Custom agent: Flag Security Vulnerabilities
Same-repository pull_request runs execute the PR’s workflow revision, and this job can request OIDC credentials for the ECR publishing role. Keep AWS-authenticated publication to trusted push runs or require an explicit trusted approval gate for PRs.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At .github/workflows/stacklet-image.yml, line 84:
<comment>Same-repository `pull_request` runs execute the PR’s workflow revision, and this job can request OIDC credentials for the ECR publishing role. Keep AWS-authenticated publication to trusted push runs or require an explicit trusted approval gate for PRs.</comment>
<file context>
@@ -0,0 +1,332 @@
+ if: vars.PUBLISH_ROLE_ARN != ''
+ permissions:
+ contents: read
+ id-token: write
+ outputs:
+ sha: ${{ steps.meta.outputs.sha }}
</file context>
There was a problem hiding this comment.
Deliberate. Same-repo PR images are intended (-pr<N>- tags for pre-merge testing). PR creation is limited to collaborators, the ECR repo is immutable so a PR cannot overwrite an existing tag, and branch protection (PRs required, admins included, no rebase-merge) stops a PR head SHA from becoming a published branch SHA. The remaining risk is a collaborator creating junk tags, which we accept.
🤖 Generated with Claude Code
| if [[ "$path" =~ ^(packages/[^/]+)/([^/]+)/ && "${shipped[${BASH_REMATCH[1]}]:-}" = "${BASH_REMATCH[2]}" ]]; then | ||
| continue | ||
| fi | ||
| if [[ "$path" =~ ^((packages|rust)/[^/]+)/ ]]; then |
There was a problem hiding this comment.
P2: Root-level files under rust/ bypass the dropped-path check, so a change to rust/.gitignore is silently accepted despite not reaching the image. Match both rust/<file> and nested Rust paths.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At scripts/stacklet-overlay-guard.sh, line 74:
<comment>Root-level files under `rust/` bypass the dropped-path check, so a change to `rust/.gitignore` is silently accepted despite not reaching the image. Match both `rust/<file>` and nested Rust paths.</comment>
<file context>
@@ -0,0 +1,90 @@
+ if [[ "$path" =~ ^(packages/[^/]+)/([^/]+)/ && "${shipped[${BASH_REMATCH[1]}]:-}" = "${BASH_REMATCH[2]}" ]]; then
+ continue
+ fi
+ if [[ "$path" =~ ^((packages|rust)/[^/]+)/ ]]; then
+ dropped[${BASH_REMATCH[1]}]+=" $path"$'\n'
+ fi
</file context>
| if [[ "$path" =~ ^((packages|rust)/[^/]+)/ ]]; then | |
| if [[ "$path" =~ ^((packages|rust)(/[^/]+)?)/ ]]; then |
There was a problem hiding this comment.
Not applying. The only file directly under rust/ is .gitignore, which is repo config, not code that ships, the same as the root-level files the guard intentionally ignores. The guard targets code changes inside packages and crates that would silently not reach the image.
🤖 Generated with Claude Code
There was a problem hiding this comment.
All reported issues were addressed across 9 files
Architecture diagram
sequenceDiagram
participant GH as GitHub Events
participant WF as Stacklet Image Workflow
participant OG as Overlay Guard Job
participant RS as Resolve Job
participant BD as Build Job
participant PB as Publish Job
participant ECR as AWS ECR
participant BS as Build Script
participant DK as Docker Build
participant IMG as Cube Image
Note over GH,ECR: CI/CD Pipeline for Stacklet Cube Image
GH->>WF: push (stacklet/integration, release/*) or PR
WF->>WF: Check event type and repo
alt Fork PR
WF->>WF: Skip workflow (no run)
else Same-repo PR or push
WF->>OG: Start overlay guard
OG->>OG: Fetch upstream release tag
OG->>OG: Check changed files under packages/ and rust/
alt Changes reach image
OG-->>WF: OK
else Changes don't reach image (e.g., Rust, non-patched)
OG-->>WF: FAIL (block workflow)
end
WF->>RS: Resolve image tag and existence
RS->>RS: Compute tag: v<version>-stacklet-<sha9> or v<version>-stacklet-pr<N>-<sha9>
RS->>ECR: Check if image tag exists
alt Tag exists
ECR-->>RS: Image found
RS-->>WF: exists=true (skip build)
else Tag not found
ECR-->>RS: ImageNotFoundException
RS-->>WF: exists=false
end
alt exists=false
WF->>BD: Build image
BD->>BS: Run build-docker.sh with image tag
BS->>BS: Read PATCHED_PACKAGES list
BS->>BS: Compile TypeScript (yarn tsc)
BS->>BS: Stage patched package dist to stacklet-patches/
BS->>BS: Copy root yarn.lock to Docker context
BS->>DK: docker build (platform: amd64, no provenance)
DK->>DK: Overlay patched packages from stacklet-patches/
alt Overlay target not installed
DK->>DK: FAIL build (explicit error)
else Copy failure
DK->>DK: FAIL build
end
DK-->>BS: Image built
BS-->>BD: Image ready
BD->>IMG: Start container (smoke test)
IMG-->>BD: /livez returns 200
BD->>BD: Save image as gzip artifact
BD-->>WF: Upload artifact
end
WF->>PB: Publish (uses artifact or existing image)
PB->>ECR: Push image to registry
ECR-->>PB: Push successful
PB->>ECR: Verify single-platform manifest
ECR-->>PB: Manifest confirmed
PB-->>WF: Complete
end
Note over WF: Only Build job runs third-party code (yarn, docker).<br/>No cloud credentials or OIDC token in Build job.
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
There was a problem hiding this comment.
All reported issues were addressed across 2 files (changes from recent commits).
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
ENG-7751
what
Add a
Stacklet Imageworkflow that builds the Stackletcubejs/cubeimage withscripts/build-docker.shand pushes it toECR. Pushes to
stacklet/integrationandrelease/**publishv<version>-stacklet-<sha9>. Same-repo PRs publishv<version>-stacklet-pr<N>-<sha9>. Fork PRs do not run.The workflow has four jobs:
scripts/stacklet-overlay-guard.sh.The script fails on any change under
packages/orrust/thatdoes not reach the image. It allows Markdown, tests,
packages/cubejs-docker/, and the TypeScript sources of patchedpackages. It compares against the upstream release tag that
matches
packages/cubejs-docker/package.json.ECR.
logger patch is present. It then starts the container, waits for
/livez, and saves the image as an artifact. It does not run whenthe tag exists.
single-platform manifest for the tag.
Only "Build" runs third-party code, and it has no cloud credentials
or OIDC token. The role, region and registry come from repo Actions
variables.
scripts/build-docker.shnow reads the overlaid packages from onePATCHED_PACKAGESlist. It compiles withyarn tsc, copies the rootlockfile into the Docker build context, and passes extra arguments to
docker build.The overlay step in
latest.Dockerfilenow replaces each patchedpackage's
dist/instead of copying over it, so a file deleted fromthe source does not survive from the npm build. It also fails the
build when a staged package is not installed or a copy fails.
A
Stacklet Lintworkflow runs pre-commit with actionlint, zizmorand shellcheck. The config limits the hooks to the Stacklet workflows
and scripts, so upstream files stay as upstream has them.
mise.tomlpins pre-commit for the workflow.
Also add
.github/CODEOWNERSand trim the Stacklet section ofpackages/cubejs-docker/DEVELOPMENT.md.why
A CI build ties each published image to one commit and a clean
checkout.
The image installs npm packages and overlays only the compiled output
of packages in
PATCHED_PACKAGES. A source change to any otherpackage does not reach the image. The guard makes that a CI failure.
The upstream workflows stay as they are, so merges of upstream
release tags do not conflict. We disable them in the repository
settings instead.
testing
A local build with the workflow's arguments passes the smoke test.
Its npm packages and compiled
server-corefiles match the currentpublished image. The only package differences are newer Debian
security updates from the base image.
This PR's own run of the workflow tests the ECR authentication and
push.
docs
packages/cubejs-docker/DEVELOPMENT.mdcovers the build script andthe guard. No other docs needed.
🤖 Generated with Claude Code