Skip to content

ci: serialize Crossplane validation - #22

Closed
patrickleet wants to merge 1 commit into
mainfrom
fix/serialize-crossplane-validation
Closed

ci: serialize Crossplane validation#22
patrickleet wants to merge 1 commit into
mainfrom
fix/serialize-crossplane-validation

Conversation

@patrickleet

@patrickleet patrickleet commented Jun 7, 2026

Copy link
Copy Markdown
Contributor

Summary

  • replace the reusable validation matrix with a local serial validation job
  • pin the CI up install to v0.46.0 with direct download retries
  • validate the Makefile example list through scripts/ci-validate-all.sh

Verification

  • up project build
  • CROSSPLANE_BIN=$(command -v crossplane) scripts/ci-validate-all.sh
  • make test
  • actionlint .github/workflows/on-pr.yaml .github/workflows/on-push-main.yaml
  • git diff --staged --check

Notes

Summary by CodeRabbit

  • Chores
    • Enhanced CI/CD workflow triggers to monitor additional repository paths for improved change detection
    • Refactored validation workflows with inline execution steps for increased stability and control
    • Pinned CLI tool versions for consistent builds and reduced compatibility issues
    • Added streamlined validation script for example configurations

@coderabbitai

coderabbitai Bot commented Jun 7, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR migrates the validation pipeline from an external reusable Crossplane workflow to an inline, locally-managed Bash script. The Makefile gains a print-examples target, the new scripts/ci-validate-all.sh performs configuration generation and composition validation with retry logic, and both PR and push-main workflows are updated to trigger on Makefile and scripts/** changes and execute the new inline validation steps.

Changes

CI Validation Pipeline Migration

Layer / File(s) Summary
Makefile example enumeration target
Makefile
Adds print-examples phony target that outputs the EXAMPLES variable entries line-by-line for the CI script to consume.
CI validation script implementation
scripts/ci-validate-all.sh
Implements Bash utilities: Crossplane CLI binary resolution, configuration_from_upbound() function that generates Configuration YAML with embedded Python dependency mapping, render_with_retry() function for retryable composition rendering with configurable backoff, and main loop that validates examples.
PR workflow trigger and validation
.github/workflows/on-pr.yaml
Expands on.pull_request.paths to include Makefile and scripts/**; replaces reusable workflow with inline steps for CLI setup (pinned Up v0.46.0, Crossplane v2.0.2), GHCR login, project build, and CI validation script execution.
Push-main workflow trigger and validation
.github/workflows/on-push-main.yaml
Expands on.push.paths to include Makefile and scripts/**; replaces reusable workflow with inline step pipeline for caching, CLI installation (same pinned versions), GHCR authentication, build, and CI script validation.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 A hop, a skip, and a script so fine,
Validates examples, line by line!
No workflows nested, just steps so clear,
With retries and Python, the path is here. ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ 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 'ci: serialize Crossplane validation' directly and clearly describes the main change: replacing parallel matrix validation with serialized/local validation steps in the CI workflow.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/serialize-crossplane-validation

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/on-pr.yaml:
- Around line 34-35: Update the workflow to pin mutable GitHub Action refs to
exact commit SHAs for actions/checkout, actions/cache, docker/login-action, and
upbound/action-up-project (replace v6/v5/v3.7.0/v1.1.2 with their corresponding
commit SHAs) and set persist-credentials: false on the actions/checkout step to
prevent the workflow token from being left in the checked-out repo; also replace
the unpinned Crossplane CLI install (the curl | sh command referencing
crossplane/crossplane@master) with a pinned commit SHA or use an official
release artifact/verified installer so the executed script is tied to a specific
commit.
- Around line 58-62: The workflow step "Install Crossplane CLI" currently pipes
the mutable installer at .../master/install.sh into sh; instead download the
installer to a file and execute that saved file, and pin the installer URL to an
immutable commit SHA instead of master. Replace the curl | sh usage by using
curl --fail --location --retry (or similar) to fetch the installer to a local
file (e.g., install.sh), make it executable, and run it with XP_VERSION=v2.0.2
./install.sh; update the URL to point to the specific commit SHA for the
installer script in the crossplane repo to ensure reproducibility.

In @.github/workflows/on-push-main.yaml:
- Around line 54-57: The "Install Crossplane CLI" step currently curls the
installer from the moving master branch; update the run command in that step
(and the identical step in the other workflow) to fetch the installer from a
pinned tag or commit URL (e.g., replace .../master/install.sh with the specific
release tag or commit SHA for Crossplane), and optionally add a checksum or
signature verification step after download before piping to sh to ensure
provenance.
- Around line 30-31: Update the checkout step and third-party action pins to
harden credentials and reduce supply-chain risk: for the Checkout step
(actions/checkout@v6) add persist-credentials: false to prevent token
persistence, and replace the tag pins for actions/checkout, actions/cache@v5,
docker/login-action@v3.7.0, and upbound/action-up-project@v1.1.2 with their
corresponding immutable commit SHAs (pin each action to a specific rev) while
preserving existing inputs and behavior.

In `@scripts/ci-validate-all.sh`:
- Around line 108-118: The render step in render_with_retry captures $? after
the entire if compound which can grab the wrong exit code; move the capture of
the render command's exit status (status=$?) into the else branch immediately
after the failed "up composition render \"$@\" >\"$output\"" so you record the
render exit code instead of the if's exit code, then proceed to rm -f "$output"
and the existing retry/return logic using that status variable.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 6ab16827-4e58-4329-a350-11c4600c682a

📥 Commits

Reviewing files that changed from the base of the PR and between 2343366 and d6be6b7.

📒 Files selected for processing (4)
  • .github/workflows/on-pr.yaml
  • .github/workflows/on-push-main.yaml
  • Makefile
  • scripts/ci-validate-all.sh

Comment on lines +34 to +35
- name: Checkout
uses: actions/checkout@v6

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate the workflow file
ls -la .github/workflows/on-pr.yaml || true

# Print relevant sections with line numbers
nl -ba .github/workflows/on-pr.yaml | sed -n '1,140p'

Repository: hops-ops/aws-crossplane-stack

Length of output: 212


🏁 Script executed:

#!/bin/bash
set -euo pipefail

nl -ba .github/workflows/on-pr.yaml | sed -n '1,160p'

Repository: hops-ops/aws-crossplane-stack

Length of output: 116


🏁 Script executed:

#!/bin/bash
set -euo pipefail

nl -ba .github/workflows/on-pr.yaml | sed -n '1,220p'

Repository: hops-ops/aws-crossplane-stack

Length of output: 116


🏁 Script executed:

#!/bin/bash
set -euo pipefail

nl -ba .github/workflows/on-pr.yaml | sed -n '1,220p'

Repository: hops-ops/aws-crossplane-stack

Length of output: 116


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "---- on-pr.yaml (lines 1-120) ----"
cat -n .github/workflows/on-pr.yaml | sed -n '1,120p'

echo "---- on-pr.yaml (lines 120-220) ----"
cat -n .github/workflows/on-pr.yaml | sed -n '120,220p' || true

Repository: hops-ops/aws-crossplane-stack

Length of output: 3931


🌐 Web query:

GitHub actions/checkout persist-credentials default value

💡 Result:

The default value for the persist-credentials input in the GitHub actions/checkout action is true [1][2][3]. When this option is enabled, the action configures the authentication token or SSH key with the local git configuration, allowing your workflow scripts to run authenticated git commands [1][4][3]. The action automatically removes these credentials during post-job cleanup [1][4]. To opt out of this behavior, you can explicitly set persist-credentials to false in your workflow file [1][5].

Citations:


🌐 Web query:

GitHub actions/checkout persist-credentials default value

💡 Result:

The default value of the GitHub Actions checkout step input persist-credentials is true.[1][2] In the actions/checkout action.yml, persist-credentials is defined with default: true.[2] The README also documents that the token/credentials are persisted locally unless you set persist-credentials: false to opt out.[1][3].

Citations:


🌐 Web query:

GitHub actions/checkout persist-credentials default value

💡 Result:

For GitHub Actions actions/checkout, the input persist-credentials default value is true. [1][2][3] This means that if you don’t set persist-credentials explicitly, the checkout action will configure the token/SSH key in the local git config (enabling authenticated git commands), and the token is removed during post-job cleanup. [2][4][1]

Citations:


Pin GitHub Actions to commit SHAs and disable persisted checkout credentials

  • .github/workflows/on-pr.yaml uses mutable action refs (actions/checkout@v6, actions/cache@v5, docker/login-action@v3.7.0, upbound/action-up-project@v1.1.2); pin each to an exact commit SHA (applies to checkout: 35, cache: 38, docker login: 64, build: 71).
  • Checkout currently leaves persist-credentials at its default (true), so the workflow token is configured in git; set persist-credentials: false to reduce token exposure since later steps execute code from the checked-out repo.
  • Crossplane CLI install executes a remote script from crossplane/crossplane@master via curl ... | sh; pin the script to a commit SHA (or use a release artifact) to avoid executing an unpinned source.
Suggested hardening
       - name: Checkout
-        uses: actions/checkout@v6
+        uses: actions/checkout@<pinned-commit-sha>
+        with:
+          persist-credentials: false
...
       - name: Cache crossplane
-        uses: actions/cache@v5
+        uses: actions/cache@<pinned-commit-sha>
...
       - name: Docker login to GHCR
-        uses: docker/login-action@v3.7.0
+        uses: docker/login-action@<pinned-commit-sha>
...
       - name: Build with up
-        uses: upbound/action-up-project@v1.1.2
+        uses: upbound/action-up-project@<pinned-commit-sha>
🧰 Tools
🪛 zizmor (1.25.2)

[warning] 34-35: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false

(artipacked)


[error] 35-35: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/on-pr.yaml around lines 34 - 35, Update the workflow to
pin mutable GitHub Action refs to exact commit SHAs for actions/checkout,
actions/cache, docker/login-action, and upbound/action-up-project (replace
v6/v5/v3.7.0/v1.1.2 with their corresponding commit SHAs) and set
persist-credentials: false on the actions/checkout step to prevent the workflow
token from being left in the checked-out repo; also replace the unpinned
Crossplane CLI install (the curl | sh command referencing
crossplane/crossplane@master) with a pinned commit SHA or use an official
release artifact/verified installer so the executed script is tied to a specific
commit.

Source: Linters/SAST tools

Comment on lines +58 to +62
- name: Install Crossplane CLI
run: |
curl -sL https://raw.githubusercontent.com/crossplane/crossplane/master/install.sh \
| XP_VERSION=v2.0.2 sh

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate and print the referenced workflow section
FILE=".github/workflows/on-pr.yaml"

echo "== File: $FILE =="
if [ -f "$FILE" ]; then
  nl -ba "$FILE" | sed -n '40,90p'
else
  echo "Missing: $FILE"
  exit 1
fi

# Search for crossplane install usage elsewhere in workflows
echo
echo "== Search: crossplane install usage =="
rg -n "crossplane.*install|install\\.sh|XP_VERSION|raw\\.githubusercontent\\.com/crossplane/crossplane" .github/workflows || true

# Search for any other pinned version usage (v2.0.2 etc.) in repo
echo
echo "== Search: XP_VERSION pin =="
rg -n "XP_VERSION|v2\.0\.2|crossplane.*v[0-9]+\.[0-9]+\.[0-9]+" .github/workflows || true

Repository: hops-ops/aws-crossplane-stack

Length of output: 175


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE=".github/workflows/on-pr.yaml"

echo "== File: $FILE =="
if [ -f "$FILE" ]; then
  awk 'NR>=40 && NR<=90 {printf "%d\t%s\n", NR, $0}' "$FILE"
else
  echo "Missing: $FILE"
  exit 1
fi

echo
echo "== Search: crossplane install usage =="
rg -n "crossplane.*install|install\\.sh|XP_VERSION|raw\\.githubusercontent\\.com/crossplane/crossplane" .github/workflows || true

echo
echo "== Search: XP_VERSION pin =="
rg -n "XP_VERSION|v2\\.0\\.2|crossplane.*v[0-9]+\\.[0-9]+\\.[0-9]+" .github/workflows || true

Repository: hops-ops/aws-crossplane-stack

Length of output: 3131


🌐 Web query:

Crossplane v2.0.2 install.sh raw.githubusercontent.com crossplane/crossplane v2.0.2 install.sh

💡 Result:

To install the Crossplane CLI version v2.0.2, you can use the official Crossplane installation script with an environment variable to specify the version [1][2]. The installation script is located at: https://raw.githubusercontent.com/crossplane/crossplane/main/install.sh Run the following command in your terminal to download and install the v2.0.2 binary: curl -sL "https://raw.githubusercontent.com/crossplane/crossplane/main/install.sh" | XP_VERSION=v2.0.2 sh After running this command, the script will download the binary, which you can then move to your local path (e.g., /usr/local/bin) to use the crossplane CLI [3][4]. Important Installation Notes: 1. Crossplane CLI vs. Crossplane Control Plane: The install script described above is for the Crossplane CLI (command-line tool) [1][2]. The Crossplane control plane itself is installed into a Kubernetes cluster using Helm [5][6]. 2. Version Compatibility: For versions prior to v2.3.0, the installation script identifies the binary as "crank" and renames it to "crossplane" during the installation process [4][7]. 3. Manual Installation: If you prefer not to use the shell script, you can manually download the binary from the official release repository [1][2]. For v2.0.2, you would look for the binary at https://releases.crossplane.io/stable/v2.0.2/bin/ [1][4].

Citations:


🌐 Web query:

Crossplane CLI install.sh security pin commit recommended curl | sh

💡 Result:

Crossplane’s recommended one-liner to install the Crossplane CLI is: curl -sL "https://raw.githubusercontent.com/crossplane/crossplane/main/install.sh" | sh [1] Security/pinning notes (based on the install script and docs): 1) The command above downloads whatever install.sh exists at the referenced URL and then executes it, so it is not “pinned” to a specific commit of the script. The script itself supports pinning the CLI version you install, via XP_VERSION and XP_CHANNEL environment variables (these choose which artifact to download) [1][2]. 2) The install.sh script downloads a platform-specific CLI binary using curl with flags that fail on HTTP errors and follow redirects (curl -sfL...). It does not, in the portions shown, include an additional integrity check such as verifying a checksum before executing the result [3]. 3) Crossplane’s docs also provide a manual alternative that avoids running the downloaded shell script: you can download the CLI binary directly from the releases repository for your channel/version [1]. Recommended safer patterns you can use: A) Pin the CLI artifact version (still runs unpinned install.sh): XP_VERSION=v1.20.0 curl -sL "https://raw.githubusercontent.com/crossplane/crossplane/main/install.sh" | sh [1] B) Pin the install script itself (unpinned CLI artifact risk remains unless you also pin XP_VERSION/XP_CHANNEL): - Prefer using a raw URL that targets a specific crossplane/crossplane commit hash for install.sh, rather than the moving /main/ (or /master/) path. The exact content of install.sh can be verified via the raw file at that commit [4]. C) Avoid executing install.sh (most “no-script” approach): - Manually download the CLI binary from the releases repository for your desired channel/version (docs call out that the release repo names the CLI binary “crank”) [1].

Citations:


🌐 Web query:

raw.githubusercontent.com/crossplane/crossplane/v2.0.2/install.sh

💡 Result:

The URL you provided (raw.githubusercontent.com/crossplane/crossplane/v2.0.2/install.sh) is an installation script for the Crossplane CLI binary (not the Crossplane Helm chart / controller itself). The script’s job is to download the appropriate CLI binary for your OS/CPU and place it as a local executable (typically into /usr/local/bin via the post-install instructions). It supports selecting release “channel” and “version” via XP_CHANNEL and XP_VERSION environment variables, and handles “older” versions that used the crank binary/artifacts vs newer versions that use crossplane/crossplane-cli artifacts. Key behaviors (from the script logic visible in the repository’s install.sh): 1) Default environment variables - XP_CHANNEL defaults to “stable”. - XP_VERSION defaults to “current”. - BIN defaults to “crossplane”. [1] 2) OS/architecture detection - The script uses uname -s and uname -m to build the OS and ARCH/OS_ARCH values used in the download URL. [1] 3) Artifact URL construction - It constructs a download URL of the form: https://<url_host>/<XP_CHANNEL>/<XP_VERSION>/<url_dir>/<OS_ARCH>/<url_file> [1] - For “compressed” downloads (COMPRESSED=true) it downloads a tarball (crossplane-cli.tar.gz); otherwise it downloads the binary directly. [1] 4) Compatibility logic for older CLI releases - If XP_VERSION is not “current”, it parses the major/minor version. - For versions earlier than 2.3 (major==2 and minor<3, or major<2), it switches to: - url_host = releases.crossplane.io - bundle_name = crank.tar.gz - BIN = crank Otherwise it uses: - url_host = cli.crossplane.io - bundle_name = crossplane-cli.tar.gz - BIN name “crossplane” [1] 5) Download, unpack, and rename - It downloads with curl -sfL and exits with an error if the requested version/channel artifacts are missing. [1] - If a tarball was downloaded, it unpacks it with tar xzf and then removes the sha256 + tarball. [1] - If the downloaded binary name is not “crossplane” (e.g., crank for older versions), it renames it to crossplane. [1] 6) Finalization - It chmod +x crossplane. [1] - It prints instructions to move it to /usr/local/bin and run crossplane --help. [1] How this relates to official documentation (current mechanism) - Crossplane’s CLI docs describe using an install script from the raw GitHub URL, e.g. curl -sL "https://raw.githubusercontent.com/crossplane/crossplane/main/install.sh" | sh, and explain that the script detects CPU architecture and downloads the latest stable release. [2] - The docs also describe XP_CHANNEL and XP_VERSION variables for selecting other CLI versions/branches, matching the same variables used inside install.sh. [2] Note on “today’s” date / version targeting - Your request specifies v2.0.2, but the web results I could retrieve in the first search round show the install.sh content from the repository’s master branch (URL ending in /crossplane/master/install.sh). The code patterns above are therefore grounded in that retrieved install.sh content; the same script template is what the docs refer to for installing any version by pinning XP_VERSION/XP_CHANNEL or by pointing at a release-specific install.sh URL. [1][2]

Citations:


🌐 Web query:

Does raw.githubusercontent.com/crossplane/crossplane/v2.0.2/install.sh exist? 404 or valid

💡 Result:

The URL raw.githubusercontent.com/crossplane/crossplane/v2.0.2/install.sh does not exist (it returns 404), while the install script does exist for other paths/tags (e.g., master has https://raw.githubusercontent.com/crossplane/crossplane/master/install.sh). [1] What I found: 1) Crossplane’s official docs reference the installer at raw.githubusercontent.com/crossplane/crossplane/main/install.sh / install.sh from the repository’s main branch (not v2.0.2). [2] 2) I could locate the repository’s install.sh content on master (but not on the v2.0.2 tag path). [1] So for v2.0.2 specifically, expect 404 at that raw.githubusercontent.com path.

Citations:


🌐 Web query:

Crossplane CLI docs pin install.sh commit hash recommended

💡 Result:

There is no official recommendation to pin the Crossplane install.sh script to a specific commit hash, and the script is intended to be fetched from the main branch to ensure the most recent installation logic is used [1][2][3]. For production or reproducible environments where you require a consistent CLI version, the official documentation instead recommends pinning the version of the Crossplane CLI binary itself using the XP_VERSION environment variable [3][4]. To install a specific version, use: curl -sL "https://raw.githubusercontent.com/crossplane/crossplane/main/install.sh" | XP_VERSION=v1.20.0 sh This ensures that regardless of updates to the install.sh script, you receive the specific binary version desired [3][4]. While the script is currently maintained in the main branch of the Crossplane repository [1][2], security discussions have noted the risks of piping directly to shell and have proposed future improvements for integrity verification [5]. Always prefer fetching official release versions as documented in the Crossplane CLI reference [3][4].

Citations:


Avoid executing an unpinned Crossplane installer via curl | sh in on-pr workflow.
Line 60 downloads https://raw.githubusercontent.com/crossplane/crossplane/master/install.sh (moving ref) and executes it via | ... sh; while XP_VERSION=v2.0.2 pins the downloaded CLI binary, the installer script itself remains mutable, weakening provenance/reproducibility.

  • Download install.sh to a file (with curl --fail --location --retry ...) and execute the saved script instead of piping to sh.
  • Pin install.sh to an immutable ref (commit SHA) rather than master (the .../v2.0.2/install.sh path returns 404).
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/on-pr.yaml around lines 58 - 62, The workflow step
"Install Crossplane CLI" currently pipes the mutable installer at
.../master/install.sh into sh; instead download the installer to a file and
execute that saved file, and pin the installer URL to an immutable commit SHA
instead of master. Replace the curl | sh usage by using curl --fail --location
--retry (or similar) to fetch the installer to a local file (e.g., install.sh),
make it executable, and run it with XP_VERSION=v2.0.2 ./install.sh; update the
URL to point to the specific commit SHA for the installer script in the
crossplane repo to ensure reproducibility.

Comment on lines +30 to +31
- name: Checkout
uses: actions/checkout@v6

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail
# Inspect the workflow file around the referenced lines
FILE=".github/workflows/on-push-main.yaml"
echo "== File: $FILE =="
if [ -f "$FILE" ]; then
  nl -ba "$FILE" | sed -n '1,120p'
else
  echo "Missing: $FILE"
fi

# Also list other workflows that might share the same patterns (for comparison)
echo
echo "== Other workflows referencing this file (if any) =="
fd .github/workflows -t f | sort

Repository: hops-ops/aws-crossplane-stack

Length of output: 182


🏁 Script executed:

#!/bin/bash
set -euo pipefail
FILE=".github/workflows/on-push-main.yaml"

echo "== File: $FILE =="
if [ -f "$FILE" ]; then
  # Print with line numbers without nl
  awk '{printf "%d\t%s\n", NR, $0}' "$FILE" | sed -n '1,140p'
else
  echo "Missing: $FILE"
fi

echo
echo "== Repo .github/workflows files =="
fd ".github/workflows" -t f | sort || true

Repository: hops-ops/aws-crossplane-stack

Length of output: 3891


🌐 Web query:

GitHub Actions checkout persist-credentials default value actions/checkout@v6

💡 Result:

For actions/checkout@v6, the persist-credentials input’s “default value” is effectively true (enabled) when you don’t set it in your workflow, because the action.yml defines persist-credentials with a default of an empty string (meaning use the action’s built-in default behavior) and the v6 README describes that credentials are persisted unless you opt out with persist-credentials: false [1][2]. What the action does: - The auth token is persisted in the local git config so that subsequent authenticated git commands work in the job; the token is removed during post-job cleanup. You can disable this by setting persist-credentials: false [2]. So if you write: - uses: actions/checkout@v6 (without a persist-credentials key), it behaves as “persist-credentials enabled” [1][2].

Citations:


Harden checkout credentials and pin third-party actions to immutable refs

  • on-push-main.yaml uses actions/checkout@v6 without persist-credentials: false, so checkout credentials are persisted by default on the main push path.
  • actions/checkout@v6 (line 31), actions/cache@v5 (line 34), docker/login-action@v3.7.0 (line 60), and upbound/action-up-project@v1.1.2 (line 67) are pinned only to version tags (not commit SHAs), leaving avoidable supply-chain risk.
🧰 Tools
🪛 zizmor (1.25.2)

[warning] 30-31: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false

(artipacked)


[error] 31-31: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/on-push-main.yaml around lines 30 - 31, Update the
checkout step and third-party action pins to harden credentials and reduce
supply-chain risk: for the Checkout step (actions/checkout@v6) add
persist-credentials: false to prevent token persistence, and replace the tag
pins for actions/checkout, actions/cache@v5, docker/login-action@v3.7.0, and
upbound/action-up-project@v1.1.2 with their corresponding immutable commit SHAs
(pin each action to a specific rev) while preserving existing inputs and
behavior.

Source: Linters/SAST tools

Comment on lines +54 to +57
- name: Install Crossplane CLI
run: |
curl -sL https://raw.githubusercontent.com/crossplane/crossplane/master/install.sh \
| XP_VERSION=v2.0.2 sh

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Show the referenced workflow lines
sed -n '1,120p' .github/workflows/on-push-main.yaml | nl -ba | sed -n '40,80p'

# Search for other uses of the Crossplane install script URL (master or otherwise)
rg -n "crossplane/crossplane/(master|main)/install\.sh|XP_VERSION|Install Crossplane CLI|install\.sh \\\"" -S .github/workflows . || true

# Search for any other Crossplane installer/provenance handling
rg -n "crossplane.*install\.sh|XP_VERSION=" -S .github/workflows . || true

Repository: hops-ops/aws-crossplane-stack

Length of output: 116


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Workflow snippet around lines 50-70 ==="
# Use sed with explicit range; no line-number printer needed
sed -n '45,75p' .github/workflows/on-push-main.yaml

echo
echo "=== Search for Crossplane installer URL patterns ==="
rg -n "crossplane/crossplane/.*/install\.sh|XP_VERSION=|Install Crossplane CLI|XP_VERSION=" .github/workflows -S || true

echo
echo "=== Search repo for 'install.sh' invocations ==="
rg -n "install\.sh" . -S || true

Repository: hops-ops/aws-crossplane-stack

Length of output: 1748


Pin the Crossplane installer source instead of using the moving master branch

The workflow downloads https://raw.githubusercontent.com/crossplane/crossplane/master/install.sh and pipes it directly to sh, making CI depend on a moving branch even though XP_VERSION=v2.0.2 is set. This same pattern exists in both .github/workflows/on-push-main.yaml and .github/workflows/on-pr.yaml.

      - name: Install Crossplane CLI
        run: |
          curl -sL https://raw.githubusercontent.com/crossplane/crossplane/master/install.sh \
            | XP_VERSION=v2.0.2 sh

Use a pinned commit/tag for the installer URL (and ideally verify provenance, e.g., checksum/signature) to keep branch-to-branch CI deterministic and safer.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/on-push-main.yaml around lines 54 - 57, The "Install
Crossplane CLI" step currently curls the installer from the moving master
branch; update the run command in that step (and the identical step in the other
workflow) to fetch the installer from a pinned tag or commit URL (e.g., replace
.../master/install.sh with the specific release tag or commit SHA for
Crossplane), and optionally add a checksum or signature verification step after
download before piping to sh to ensure provenance.

Comment on lines +108 to +118
if up composition render "$@" >"$output"; then
cat "$output"
rm -f "$output"
return 0
fi

status=$?
rm -f "$output"
if [ "$attempt" -ge "$max_attempts" ]; then
return "$status"
fi

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

render_with_retry can return the wrong status after a failed render.

Line 114 captures $? after the if compound, so the final failure path can return a non-render status and let validation proceed incorrectly. Capture the render exit code inside the else branch.

Suggested fix
 	while true; do
-		if up composition render "$@" >"$output"; then
+		if up composition render "$@" >"$output"; then
 			cat "$output"
 			rm -f "$output"
 			return 0
+		else
+			status=$?
 		fi
-
-		status=$?
 		rm -f "$output"
 		if [ "$attempt" -ge "$max_attempts" ]; then
 			return "$status"
 		fi
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/ci-validate-all.sh` around lines 108 - 118, The render step in
render_with_retry captures $? after the entire if compound which can grab the
wrong exit code; move the capture of the render command's exit status
(status=$?) into the else branch immediately after the failed "up composition
render \"$@\" >\"$output\"" so you record the render exit code instead of the
if's exit code, then proceed to rm -f "$output" and the existing retry/return
logic using that status variable.

@github-actions

github-actions Bot commented Jun 7, 2026

Copy link
Copy Markdown

Published Crossplane Package

The following Crossplane package was published as part of this PR:

Package: ghcr.io/hops-ops/aws-crossplane-stack:pr-22-0dd5d6fbc4fe8c435a51c8a9582d00477daf98da

View Package

@patrickleet patrickleet closed this Jun 7, 2026
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.

1 participant