Compare commits

..

3 Commits

Author SHA1 Message Date
Chris Farhood 75cf41ef4d fix: sync pnpm-lock.yaml after removing tar and undici deps
The pnpm-lock.yaml was out of sync with package.json after tar and undici
were removed. Regenerated to resolve pnpm install failure in CI.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-05-04 15:19:26 +00:00
Chris Farhood a324ee621b fix: add markdownlint config to resolve CI failures
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-05-04 05:24:26 +00:00
Chris Farhood 0c521be1a1 Remove duplicate tar/undici from devDependencies (already in pnpm.overrides)
Consolidates dual override blocks by removing the duplicate entries
from devDependencies. These packages are already pinned via pnpm.overrides
and should not appear in devDependencies.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-05-04 02:21:10 +00:00
10 changed files with 23 additions and 753 deletions
+3 -216
View File
@@ -2,223 +2,10 @@ name: CI
on:
push:
branches: ['**']
branches: [main]
pull_request:
branches: [main, dev, uat]
workflow_dispatch:
inputs:
node-version:
description: 'Node.js version to use'
required: false
type: string
default: '22'
workflow_call:
inputs:
node-version:
description: 'Node.js version to use'
required: false
type: string
default: '22'
permissions:
contents: read
branches: [main]
jobs:
ci:
runs-on: ubuntu-latest
timeout-minutes: 10
container: node:22-slim
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Install Python
run: apt-get update && apt-get install -y --no-install-recommends python3 python3-yaml
- name: Validate artifacthub-pkg.yml
run: |
python3 - <<'EOF'
import sys, re
try:
import yaml
except ImportError:
print("::warning::PyYAML not available, skipping artifacthub-pkg.yml validation")
sys.exit(0)
try:
with open("artifacthub-pkg.yml") as f:
pkg = yaml.safe_load(f)
except FileNotFoundError:
print("::error::artifacthub-pkg.yml not found")
sys.exit(1)
except yaml.YAMLError as e:
print(f"::error::artifacthub-pkg.yml is invalid YAML: {e}")
sys.exit(1)
errors = []
for field in ["version", "name", "description", "homeURL"]:
if not pkg.get(field):
errors.append(f"Missing required field: {field}")
version = pkg.get("version", "")
if version and not re.match(r'^\d+\.\d+\.\d+$', str(version)):
errors.append(f"version '{version}' is not SemVer (expected X.Y.Z)")
annotations = pkg.get("annotations", {}) or {}
archive_url = annotations.get("headlamp/plugin/archive-url", "")
archive_checksum = annotations.get("headlamp/plugin/archive-checksum", "")
if not archive_url:
errors.append("Missing annotation: headlamp/plugin/archive-url")
if not archive_checksum:
errors.append("Missing annotation: headlamp/plugin/archive-checksum")
elif not re.match(r'^sha256:[0-9a-f]{64}$', str(archive_checksum)):
errors.append(f"archive-checksum has unexpected format: '{archive_checksum}' (expected sha256:<64 hex chars>)")
if errors:
for e in errors:
print(f"::error::{e}")
sys.exit(1)
print(f"artifacthub-pkg.yml valid: name={pkg['name']} version={pkg['version']}")
EOF
- name: Detect package manager
id: pkg-manager
run: |
if [ -f "pnpm-lock.yaml" ]; then
echo "manager=pnpm" >> $GITHUB_OUTPUT
PM=$(python3 -c "import json,sys; d=json.load(open('package.json')); print('true' if d.get('packageManager','').startswith('pnpm@') else 'false')" 2>/dev/null || echo "false")
echo "has_package_manager=$PM" >> $GITHUB_OUTPUT
else
echo "manager=npm" >> $GITHUB_OUTPUT
echo "has_package_manager=false" >> $GITHUB_OUTPUT
fi
- name: Setup Node
uses: actions/setup-node@v6
with:
node-version: ${{ inputs.node-version || '22' }}
cache: ${{ steps.pkg-manager.outputs.manager == 'npm' && 'npm' || '' }}
- name: Setup pnpm (via Corepack, reads version from packageManager field)
if: steps.pkg-manager.outputs.manager == 'pnpm' && steps.pkg-manager.outputs.has_package_manager == 'true'
run: |
npm install -g corepack
corepack enable pnpm
corepack install
- name: Setup pnpm (version latest)
if: steps.pkg-manager.outputs.manager == 'pnpm' && steps.pkg-manager.outputs.has_package_manager == 'false'
uses: pnpm/action-setup@v5
with:
run_install: false
version: latest
- name: Get pnpm store directory
id: pnpm-store
if: steps.pkg-manager.outputs.manager == 'pnpm'
run: echo "dir=$(pnpm store path --silent)" >> $GITHUB_OUTPUT
- name: Cache pnpm store
if: steps.pkg-manager.outputs.manager == 'pnpm'
uses: actions/cache@v5
with:
path: ${{ steps.pnpm-store.outputs.dir }}
key: ${{ runner.os }}-pnpm-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-
- name: Validate pnpm lockfile freshness
if: steps.pkg-manager.outputs.manager == 'pnpm'
run: |
if [ ! -f "pnpm-lock.yaml" ]; then
echo "No pnpm-lock.yaml found, skipping lockfile freshness check"
exit 0
fi
if ! grep -q 'overrides:' pnpm-lock.yaml 2>/dev/null; then
echo "No overrides section in pnpm-lock.yaml, skipping lockfile freshness check"
exit 0
fi
echo "Detected pnpm-lock.yaml with overrides section. Checking lockfile freshness..."
ERR_FILE=$(mktemp)
if pnpm install --frozen-lockfile 2>&1 | tee "$ERR_FILE"; then
echo "Lockfile is fresh."
else
if grep -q "CONFIG_MISMATCH\|EBADLOCKFILE\|ERR_PNPM_LOCKFILE" "$ERR_FILE"; then
echo ""
echo "::error::pnpm-lock.yaml is out of sync with package.json overrides."
echo "::error::Run 'pnpm install' to regenerate the lockfile and commit the updated pnpm-lock.yaml."
rm -f "$ERR_FILE"
exit 1
fi
rm -f "$ERR_FILE"
echo "::warning::Install failed with a different error. Will retry in the Install dependencies step."
fi
- name: Install dependencies
run: |
max_attempts=3
attempt=1
while [ $attempt -le $max_attempts ]; do
echo "Attempt $attempt of $max_attempts"
if [ "${{ steps.pkg-manager.outputs.manager }}" = "pnpm" ]; then
pnpm install --frozen-lockfile && break
else
npm ci && break
fi
if [ $attempt -lt $max_attempts ]; then
echo "::warning::Install step failed on attempt $attempt. Retrying in 5 seconds..."
sleep 5
fi
attempt=$((attempt + 1))
done
if [ $attempt -gt $max_attempts ]; then
echo "::error::Install step failed after $max_attempts attempts."
exit 1
fi
- name: Build plugin
run: npx @kinvolk/headlamp-plugin build
- name: Lint
run: |
if [ "${{ steps.pkg-manager.outputs.manager }}" = "pnpm" ]; then
pnpm run lint
else
npm run lint
fi
- name: Type-check
run: |
if [ "${{ steps.pkg-manager.outputs.manager }}" = "pnpm" ]; then
pnpm run tsc
else
npm run tsc
fi
- name: Format check
run: |
if [ "${{ steps.pkg-manager.outputs.manager }}" = "pnpm" ]; then
pnpm run format:check
else
npm run format:check
fi
- name: Run tests
run: |
if [ "${{ steps.pkg-manager.outputs.manager }}" = "pnpm" ]; then
pnpm test
else
npm test
fi
- name: Security audit
run: |
if [ "${{ steps.pkg-manager.outputs.manager }}" = "pnpm" ]; then
npx audit-ci --pnpm --audit-level=high --config ./audit-ci.jsonc
else
npx audit-ci --npm --audit-level=high --config ./audit-ci.jsonc
fi
uses: privilegedescalation/.github/.github/workflows/plugin-ci.yaml@main
-116
View File
@@ -1,116 +0,0 @@
name: Promotion Gate
# dev PRs: no gate (engineer self-merges).
# uat PRs: QA approval required.
# main PRs: UAT approval required (uat→main promotions).
on:
pull_request_review:
types: [submitted, dismissed]
pull_request:
branches: [uat, main]
types: [opened, reopened, synchronize]
jobs:
promotion-gate:
name: Promotion Gate
runs-on: ubuntu-latest
container: ubuntu:latest
timeout-minutes: 5
steps:
- name: Install dependencies
run: apt-get update -qq && apt-get install -y --no-install-recommends ca-certificates curl jq
- name: Check promotion approval
env:
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
PR_NUMBER: ${{ github.event.pull_request.number }}
REPO: ${{ github.repository }}
BASE_REF: ${{ github.base_ref }}
run: |
if [ -z "${PR_NUMBER}" ] || [ "${PR_NUMBER}" = "null" ]; then
echo "::notice::No PR number in context. Skipping promotion gate."
exit 0
fi
echo "Checking promotion gate for PR #${PR_NUMBER} targeting ${BASE_REF} in ${REPO}"
if [ -z "${BASE_REF}" ] && [ -n "${PR_NUMBER}" ] && [ "${PR_NUMBER}" != "null" ]; then
BASE_REF=$(curl -sf \
-H "Authorization: token ${GITEA_TOKEN}" \
-H "Accept: application/json" \
"https://git.farh.net/api/v1/repos/${REPO}/pulls/${PR_NUMBER}" | jq -r '.base.ref')
echo "BASE_REF was empty; resolved from PR #${PR_NUMBER} API: ${BASE_REF}"
fi
# Determine required reviewer based on target branch
case "${BASE_REF}" in
dev)
echo "Target is dev — no review required. Engineers self-merge."
exit 0
;;
uat)
REQUIRED_REVIEWER="pe_regina"
GATE_NAME="QA"
;;
main)
REQUIRED_REVIEWER="pe_regina"
GATE_NAME="QA"
# For plugin repos (Pipeline A), UAT approval is needed for uat→main
# Check if the source branch is uat
SOURCE_REF=$(curl -sf \
-H "Authorization: token ${GITEA_TOKEN}" \
-H "Accept: application/json" \
"https://git.farh.net/api/v1/repos/${REPO}/pulls/${PR_NUMBER}" | jq -r '.head.ref')
if [ "${SOURCE_REF}" = "uat" ]; then
REQUIRED_REVIEWER="pe_patty"
GATE_NAME="UAT"
fi
;;
*)
echo "::notice::Target branch '${BASE_REF}' has no promotion gate configured."
exit 0
;;
esac
echo "Required reviewer: ${REQUIRED_REVIEWER} (${GATE_NAME})"
# For uat→main promotions, pe_patty may not be able to review (bot account).
# Accept pe_nancy (CTO) as a valid alternative reviewer.
ALT_REVIEWER=""
if [ "${REQUIRED_REVIEWER}" = "pe_patty" ]; then
ALT_REVIEWER="pe_nancy"
fi
REVIEWS=$(curl -sf \
-H "Authorization: token ${GITEA_TOKEN}" \
-H "Accept: application/json" \
"https://git.farh.net/api/v1/repos/${REPO}/pulls/${PR_NUMBER}/reviews")
if [ -z "${REVIEWS}" ] || [ "${REVIEWS}" = "null" ]; then
echo "::warning::Could not fetch reviews for PR #${PR_NUMBER}."
exit 1
fi
REVIEWER_APPROVED=$(echo "${REVIEWS}" | jq -r --arg user "${REQUIRED_REVIEWER}" \
'[.[] | select(.user.login == $user)] | last | if .state then .state == "APPROVED" else false end')
echo "${GATE_NAME} (${REQUIRED_REVIEWER}) approved: ${REVIEWER_APPROVED}"
# Fallback: check if CTO approved as alternative for uat→main
if [ "${REVIEWER_APPROVED}" != "true" ] && [ -n "${ALT_REVIEWER}" ]; then
REVIEWER_APPROVED=$(echo "${REVIEWS}" | jq -r --arg user "${ALT_REVIEWER}" \
'[.[] | select(.user.login == $user)] | last | if .state then .state == "APPROVED" else false end')
if [ "${REVIEWER_APPROVED}" = "true" ]; then
echo "CTO (${ALT_REVIEWER}) approved as fallback for UAT gate."
fi
fi
if [ "${REVIEWER_APPROVED}" = "true" ]; then
echo "Promotion gate passed: ${GATE_NAME} has approved."
else
echo "Promotion gate failed: waiting for ${GATE_NAME} approval from ${REQUIRED_REVIEWER}."
exit 1
fi
+8 -382
View File
@@ -7,392 +7,18 @@ on:
description: 'Release version (e.g. 1.0.0)'
required: true
type: string
repository_dispatch:
types: [release]
permissions:
contents: write
pull-requests: write
jobs:
check-secrets:
runs-on: runners-privilegedescalation
outputs:
ready: ${{ steps.check.outputs.ready }}
steps:
- name: Verify GITEA_RELEASE_TOKEN is configured
id: check
env:
GITEA_RELEASE_TOKEN: ${{ secrets.GITEA_RELEASE_TOKEN }}
run: |
if [ -z "$GITEA_RELEASE_TOKEN" ]; then
echo "::notice::GITEA_RELEASE_TOKEN org secret is not configured (see PRI-1533). Release skipped — no artifacts will be created."
echo "ready=false" >> $GITHUB_OUTPUT
else
echo "ready=true" >> $GITHUB_OUTPUT
fi
ci:
needs: check-secrets
if: needs.check-secrets.outputs.ready == 'true'
uses: ./.github/workflows/ci.yaml
with:
node-version: '22'
check-token-permissions:
needs: check-secrets
if: needs.check-secrets.outputs.ready == 'true'
runs-on: runners-privilegedescalation
outputs:
has_write: ${{ steps.check.outputs.has_write }}
steps:
- name: Check write permissions via API
id: check
env:
GITEA_TOKEN: ${{ secrets.GITEA_RELEASE_TOKEN }}
REPO: ${{ github.repository }}
run: |
HTTP_CODE=$(curl -sf -o /dev/null -w "%{http_code}" \
-X POST \
-H "Authorization: token ${GITEA_TOKEN}" \
-H "Accept: application/json" \
"https://git.farh.net/api/v1/repos/${REPO}/git/refs" \
-d '{"ref":"refs/heads/_release_check","sha":"${{ github.sha }}"}')
if [ "$HTTP_CODE" = "201" ]; then
echo "::notice::Token has write permission — cleaning up test ref."
curl -sf -o /dev/null -w "%{http_code}" \
-X DELETE \
-H "Authorization: token ${GITEA_TOKEN}" \
"https://git.farh.net/api/v1/repos/${REPO}/git/refs/heads/_release_check"
echo "has_write=true" >> $GITHUB_OUTPUT
elif [ "$HTTP_CODE" = "403" ]; then
echo "::error::Token lacks write permission. Release cannot push tags or branches."
echo "has_write=false" >> $GITHUB_OUTPUT
exit 1
else
echo "::warning::Unexpected response ($HTTP_CODE) when checking write permission."
echo "has_write=false" >> $GITHUB_OUTPUT
exit 1
fi
check-tag:
needs: check-secrets
if: needs.check-secrets.outputs.ready == 'true'
runs-on: runners-privilegedescalation
outputs:
skip: ${{ steps.check.outputs.skip }}
steps:
- name: Check if tag already exists
id: check
env:
GITEA_TOKEN: ${{ secrets.GITEA_RELEASE_TOKEN }}
REPO: ${{ github.repository }}
run: |
HTTP_CODE=$(curl -sf -o /dev/null -w "%{http_code}" \
-H "Authorization: token ${GITEA_TOKEN}" \
"https://git.farh.net/api/v1/repos/${REPO}/git/refs/tags/v${{ inputs.version }}")
if [ "$HTTP_CODE" = "200" ]; then
echo "::notice::Tag v${{ inputs.version }} already exists. Release skipped (not an error)."
echo "skip=true" >> $GITHUB_OUTPUT
else
echo "skip=false" >> $GITHUB_OUTPUT
fi
release:
needs: [ci, check-tag, check-secrets, check-token-permissions]
if: needs.check-secrets.outputs.ready == 'true' && needs.check-tag.outputs.skip != 'true' && needs.check-token-permissions.outputs.has_write == 'true'
runs-on: runners-privilegedescalation
timeout-minutes: 10
steps:
- name: Validate version format
run: |
if [[ ! "${{ inputs.version }}" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "Error: Version must be in X.Y.Z format"
exit 1
fi
- name: Checkout
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Detect package manager
id: pkg-manager
run: |
if [ -f "pnpm-lock.yaml" ]; then
echo "manager=pnpm" >> $GITHUB_OUTPUT
echo "lockfile=pnpm-lock.yaml" >> $GITHUB_OUTPUT
PM=$(python3 -c "import json,sys; d=json.load(open('package.json')); print('true' if d.get('packageManager','').startswith('pnpm@') else 'false')" 2>/dev/null || echo "false")
echo "has_package_manager=$PM" >> $GITHUB_OUTPUT
else
echo "manager=npm" >> $GITHUB_OUTPUT
echo "lockfile=package-lock.json" >> $GITHUB_OUTPUT
echo "has_package_manager=false" >> $GITHUB_OUTPUT
fi
- name: Setup Node
uses: actions/setup-node@v6
with:
node-version: '22'
cache: ${{ steps.pkg-manager.outputs.manager == 'npm' && 'npm' || '' }}
- name: Setup pnpm (via Corepack)
if: steps.pkg-manager.outputs.manager == 'pnpm' && steps.pkg-manager.outputs.has_package_manager == 'true'
run: |
npm install -g corepack
corepack enable pnpm
corepack install
- name: Setup pnpm (version latest)
if: steps.pkg-manager.outputs.manager == 'pnpm' && steps.pkg-manager.outputs.has_package_manager == 'false'
uses: pnpm/action-setup@v5
with:
run_install: false
version: latest
- name: Get pnpm store directory
id: pnpm-store
if: steps.pkg-manager.outputs.manager == 'pnpm'
run: echo "dir=$(pnpm store path --silent)" >> $GITHUB_OUTPUT
- name: Cache pnpm store
if: steps.pkg-manager.outputs.manager == 'pnpm'
uses: actions/cache@v5
with:
path: ${{ steps.pnpm-store.outputs.dir }}
key: ${{ runner.os }}-pnpm-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-
- name: Configure Git
env:
GITEA_TOKEN: ${{ secrets.GITEA_RELEASE_TOKEN }}
run: |
git config --global user.name "github-actions[bot]"
git config --global user.email "github-actions[bot]@users.noreply.github.com"
git config --global --add safe.directory "$GITHUB_WORKSPACE"
git remote set-url origin "https://x-access-token:${GITEA_TOKEN}@git.farh.net/${{ github.repository }}.git"
- name: Update version in package.json
run: |
if [ "${{ steps.pkg-manager.outputs.manager }}" = "pnpm" ]; then
pnpm version ${{ inputs.version }} --no-git-tag-version --allow-same-version
else
npm version ${{ inputs.version }} --no-git-tag-version --allow-same-version
fi
- name: Update artifacthub-pkg.yml
env:
REPO: ${{ github.repository }}
run: |
VERSION="${{ inputs.version }}"
if [ -f artifacthub-pkg.yml ]; then
PKG_NAME=$(grep '^name:' artifacthub-pkg.yml | cut -d: -f2 | tr -d ' "')
else
PKG_NAME=$(jq -r .name package.json | sed 's|^@[^/]*/||')
fi
RELEASE_URL="https://git.farh.net/${REPO}/releases/download/v${VERSION}/${PKG_NAME}-${VERSION}.tar.gz"
sed -i "s/^version:.*/version: \"${VERSION}\"/" artifacthub-pkg.yml
sed -i "s|headlamp/plugin/archive-url:.*|headlamp/plugin/archive-url: \"${RELEASE_URL}\"|" artifacthub-pkg.yml
- name: Update appVersion from upstream release
env:
GITEA_TOKEN: ${{ secrets.GITEA_RELEASE_TOKEN }}
run: |
APP_VERSION=$(curl -sf \
-H "Authorization: token ${GITEA_TOKEN}" \
"https://git.farh.net/api/v1/repos/fenio/tns-csi/releases/latest" | jq -r '.tag_name | ltrimstr("v")')
if [ -z "$APP_VERSION" ] || [ "$APP_VERSION" = "null" ]; then
echo "::warning::Could not fetch latest upstream release, skipping appVersion update"
else
sed -i "s|^appVersion:.*|appVersion: \"${APP_VERSION}\"|" artifacthub-pkg.yml
echo "appVersion set to ${APP_VERSION}"
fi
- name: Install dependencies
run: |
max_attempts=3
attempt=1
while [ $attempt -le $max_attempts ]; do
echo "Attempt $attempt of $max_attempts"
if [ "${{ steps.pkg-manager.outputs.manager }}" = "pnpm" ]; then
pnpm install --frozen-lockfile && break
else
npm ci && break
fi
if [ $attempt -lt $max_attempts ]; then
echo "::warning::Install step failed on attempt $attempt. Retrying in 5 seconds..."
sleep 5
fi
attempt=$((attempt + 1))
done
if [ $attempt -gt $max_attempts ]; then
echo "::error::Install step failed after $max_attempts attempts."
exit 1
fi
- name: Build plugin
run: npx @kinvolk/headlamp-plugin build
- name: Package plugin
run: npx @kinvolk/headlamp-plugin package
- name: Prepare release tarball
run: |
VERSION="${{ inputs.version }}"
if [ -f artifacthub-pkg.yml ]; then
PKG_NAME=$(grep '^name:' artifacthub-pkg.yml | cut -d: -f2 | tr -d ' "')
else
PKG_NAME=$(jq -r .name package.json | sed 's|^@[^/]*/||')
fi
TARBALL="${PKG_NAME}-${VERSION}.tar.gz"
for f in *.tar.gz; do
[ "$f" != "$TARBALL" ] && mv "$f" "$TARBALL"
done
if [ ! -f "$TARBALL" ]; then
echo "Error: Expected tarball $TARBALL not found"
ls -la *.tar.gz 2>/dev/null || echo "No .tar.gz files found"
exit 1
fi
echo "TARBALL=$TARBALL" >> $GITHUB_ENV
echo "PKG_NAME=$PKG_NAME" >> $GITHUB_ENV
- name: Validate tarball
run: |
echo "Tarball: ${{ env.TARBALL }}"
ls -lh "${{ env.TARBALL }}"
tar -tzf "${{ env.TARBALL }}" | head -20
tar -tzf "${{ env.TARBALL }}" | grep -q "main.js" || { echo "Error: main.js not found in tarball"; exit 1; }
- name: Compute checksum
run: |
CHECKSUM=$(sha256sum "${{ env.TARBALL }}" | awk '{print $1}')
echo "CHECKSUM=$CHECKSUM" >> $GITHUB_ENV
sed -i "s|headlamp/plugin/archive-checksum:.*|headlamp/plugin/archive-checksum: sha256:${CHECKSUM}|" artifacthub-pkg.yml
- name: Commit and tag
env:
GITEA_TOKEN: ${{ secrets.GITEA_RELEASE_TOKEN }}
run: |
VERSION="${{ inputs.version }}"
BRANCH="release/v${VERSION}"
if git ls-remote --exit-code origin "refs/heads/$BRANCH" 2>/dev/null; then
echo "::notice::Branch $BRANCH already exists — deleting for clean re-trigger."
git push origin --delete "$BRANCH"
fi
git checkout -b "$BRANCH"
git add package.json "${{ steps.pkg-manager.outputs.lockfile }}" artifacthub-pkg.yml
git commit -m "release: v${VERSION}"
git tag "v${VERSION}"
git push origin "$BRANCH"
git push origin "refs/tags/v${VERSION}"
- name: Create Gitea Release
env:
GITEA_TOKEN: ${{ secrets.GITEA_RELEASE_TOKEN }}
REPO: ${{ github.repository }}
run: |
VERSION="${{ inputs.version }}"
TARBALL="${{ env.TARBALL }}"
RESPONSE=$(curl -sf -X POST \
-H "Authorization: token ${GITEA_TOKEN}" \
-H "Content-Type: application/json" \
"https://git.farh.net/api/v1/repos/${REPO}/releases" \
-d "{
\"tag_name\": \"v${VERSION}\",
\"name\": \"Release v${VERSION}\",
\"draft\": false,
\"prerelease\": false
}")
if [ -z "$RESPONSE" ]; then
echo "::warning::Release creation returned empty response (may already exist)"
else
echo "::notice::Release v${VERSION} created successfully"
fi
UPLOAD_URL=$(echo "$RESPONSE" | jq -r '.upload_url' 2>/dev/null || echo "")
if [ -n "$UPLOAD_URL" ] && [ "$UPLOAD_URL" != "null" ]; then
UPLOAD_URL="${UPLOAD_URL%%\{*}"
curl -sf -X POST \
-H "Authorization: token ${GITEA_TOKEN}" \
-H "Content-Type: application/octet-stream" \
"${UPLOAD_URL}?name=${TARBALL}" \
--data-binary "@${TARBALL}"
echo "::notice::Tarball uploaded successfully"
fi
- name: Create PR for version bump
env:
GITEA_TOKEN: ${{ secrets.GITEA_RELEASE_TOKEN }}
REPO: ${{ github.repository }}
run: |
set -o pipefail
VERSION="${{ inputs.version }}"
BODY=$(printf "Automated version bump and checksum update for v%s.\n\ncc @cpfarhood" "${VERSION}")
RESPONSE=$(curl -sf -X POST \
-H "Authorization: token ${GITEA_TOKEN}" \
-H "Content-Type: application/json" \
"https://git.farh.net/api/v1/repos/${REPO}/pulls" \
-d "{
\"title\": \"release: v${VERSION}\",
\"body\": \"$BODY\",
\"base\": \"main\",
\"head\": \"release/v${VERSION}\"
}")
PR_NUMBER=$(echo "$RESPONSE" | jq -r '.number' 2>/dev/null || echo "")
if [ -z "$PR_NUMBER" ] || [ "$PR_NUMBER" = "null" ]; then
EXISTING=$(curl -sf \
-H "Authorization: token ${GITEA_TOKEN}" \
"https://git.farh.net/api/v1/repos/${REPO}/pulls?state=open&base=main&head=release/v${VERSION}" \
| jq -r '.[0].number' 2>/dev/null || echo "")
if [ -n "$EXISTING" ] && [ "$EXISTING" != "null" ]; then
PR_NUMBER="$EXISTING"
echo "::notice::Open PR #${PR_NUMBER} for release/v${VERSION} already exists — skipping creation."
else
echo "::error::Could not determine PR number for release/v${VERSION}."
exit 1
fi
fi
echo "::notice::Working with PR #${PR_NUMBER}"
PR_STATE=$(curl -sf \
-H "Authorization: token ${GITEA_TOKEN}" \
"https://git.farh.net/api/v1/repos/${REPO}/pulls/${PR_NUMBER}" \
| jq -r '.state' 2>/dev/null || echo "")
if [ "$PR_STATE" = "merged" ]; then
echo "::notice::PR #${PR_NUMBER} was already merged. Nothing to do."
exit 0
fi
MERGE_RESULT=$(curl -sf -X POST \
-H "Authorization: token ${GITEA_TOKEN}" \
-H "Content-Type: application/json" \
"https://git.farh.net/api/v1/repos/${REPO}/pulls/${PR_NUMBER}/merge" \
-d '{"do": "squash"}')
if echo "$MERGE_RESULT" | jq -e '.merged' 2>/dev/null; then
echo "PR merged successfully."
else
if [ "$PR_STATE" = "merged" ]; then
echo "PR was already merged."
else
echo "::warning::Merge response: $MERGE_RESULT"
fi
fi
- name: Verify checksums are consistent (main == tag == tarball)
env:
GITEA_TOKEN: ${{ secrets.GITEA_RELEASE_TOKEN }}
REPO: ${{ github.repository }}
run: |
VERSION="${{ inputs.version }}"
TARBALL_CS=$(sha256sum "${{ env.TARBALL }}" | awk '{print $1}')
TAG_CS=$(git show "v${VERSION}:artifacthub-pkg.yml" 2>/dev/null \
| grep "archive-checksum" | awk '{print $2}' | sed 's/sha256://')
MAIN_CS=$(git fetch origin main 2>/dev/null; git show "origin/main:artifacthub-pkg.yml" \
| grep "archive-checksum" | awk '{print $2}' | sed 's/sha256://')
echo "Tarball SHA256 : $TARBALL_CS"
echo "Tag artifacthub: $TAG_CS"
echo "Main artifacthub: $MAIN_CS"
FAIL=0
[ "$TARBALL_CS" != "$TAG_CS" ] && echo "ERROR: tag checksum mismatch!" && FAIL=1
[ "$TARBALL_CS" != "$MAIN_CS" ] && echo "ERROR: main checksum mismatch!" && FAIL=1
[ "$FAIL" = "1" ] && exit 1
echo "All checksums consistent — ArtifactHub will index correctly."
uses: privilegedescalation/.github/.github/workflows/plugin-release.yaml@main
secrets:
RELEASE_APP_ID: ${{ secrets.RELEASE_APP_ID }}
RELEASE_APP_PRIVATE_KEY: ${{ secrets.RELEASE_APP_PRIVATE_KEY }}
with:
version: ${{ inputs.version || github.event.client_payload.version }}
-1
View File
@@ -32,4 +32,3 @@ gh workflow run Release --field version=0.1.0
## License
Apache-2.0
+5 -5
View File
@@ -1,4 +1,4 @@
version: "0.1.3"
version: "0.1.0"
name: headlamp-argocd
displayName: ArgoCD Headlamp Plugin
createdAt: "2026-04-21T00:00:00Z"
@@ -26,10 +26,10 @@ maintainers:
provider:
name: privilegedescalation
annotations:
headlamp/plugin/archive-url: "https://github.com/privilegedescalation/headlamp-argocd-plugin/releases/download/v0.1.3/privilegedescalation-headlamp-argocd-plugin-0.1.3.tar.gz"
headlamp/plugin/archive-checksum: sha256:cf96084b79a76b341b5f08d4e17ccf77b5de20f4178061ddc5b5e8dfa81d2743
headlamp/plugin/archive-url: "https://github.com/privilegedescalation/headlamp-argocd-plugin/releases/download/v0.1.0/headlamp-argocd-0.1.0.tar.gz"
headlamp/plugin/archive-checksum: "sha256:1f4df43f79b795bdf4f70e1e3aa5bacadf689ea5584fdadf92fb677faab21c2c"
headlamp/plugin/version-compat: ">=0.26"
headlamp/plugin/distro-compat: "in-cluster"
changes:
- kind: fixed
description: "Fix archive URL to point to GitHub v0.1.3 release and bump version"
- kind: added
description: "Initial v0.1.0 release"
+2 -2
View File
@@ -1,6 +1,6 @@
# Artifact Hub repository metadata
# https://artifacthub.io/docs/topics/repositories/#repository-metadata-file
repositoryID: "3648e8a8-54f7-474c-9977-00ec3b4ea1e1"
repositoryID: ""
owners:
- name: privilegedescalation
email: chris@farhood.org
email: chris@farhood.org
-20
View File
@@ -1,20 +0,0 @@
{
// Allowlist for inherited dev-dependency CVEs from @kinvolk/headlamp-plugin
// CTO decision (PRI-854): these high-severity vulns are dev/build-time only,
// trace to @kinvolk/headlamp-plugin transitive deps (Picomatch, Vite, lodash),
// and do NOT ship in production plugin artifacts.
"allowlist": [
{
"id": "GHSA-hhpm-516h-p3p6",
"reason": "Picomatch ReDoS: devDependency only, does not ship in production plugin bundle"
},
{
"id": "GHSA-36xf-7xpp-53w5",
"reason": "Vite arbitrary file read: devDependency only, does not ship in production plugin bundle"
},
{
"id": "GHSA-jf8v-p3pp-93qh",
"reason": "lodash code injection via _.template: devDependency only, does not ship in production plugin bundle"
}
]
}
+3 -4
View File
@@ -1,6 +1,6 @@
{
"name": "@privilegedescalation/headlamp-argocd-plugin",
"version": "0.1.2",
"version": "0.1.0",
"description": "Headlamp plugin for ArgoCD visibility — monitors ArgoCD Applications, Rollouts, and health status",
"repository": {
"type": "git",
@@ -33,8 +33,7 @@
"overrides": {
"tar": "^7.5.11",
"undici": "^7.24.3",
"flatted": "^3.4.2",
"elliptic": ">=6.6.1"
"flatted": "^3.4.2"
}
},
"devDependencies": {
@@ -56,4 +55,4 @@
"typescript": "~5.6.2",
"vitest": "^3.0.5"
}
}
}
+2 -3
View File
@@ -8,7 +8,6 @@ overrides:
tar: ^7.5.11
undici: ^7.24.3
flatted: ^3.4.2
elliptic: '>=6.6.1'
importers:
@@ -6230,7 +6229,7 @@ snapshots:
jsdom: 24.1.3
jsonpath-plus: 10.4.0
lodash: 4.18.1
material-react-table: 2.13.3(0078ddeddc9e779fa84c03996c1db10e)
material-react-table: 2.13.3(330725fe5432f245d076f0c0dda1a7a7)
monaco-editor: 0.52.2
msw: 2.4.9(typescript@5.6.2)
msw-storybook-addon: 2.0.3(msw@2.4.9(typescript@5.6.3))
@@ -9932,7 +9931,7 @@ snapshots:
'@types/minimatch': 3.0.5
minimatch: 3.1.5
material-react-table@2.13.3(0078ddeddc9e779fa84c03996c1db10e):
material-react-table@2.13.3(330725fe5432f245d076f0c0dda1a7a7):
dependencies:
'@emotion/react': 11.14.0(@types/react@18.3.28)(react@18.3.1)
'@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@18.3.1))(@types/react@18.3.28)(react@18.3.1)
-4
View File
@@ -1,4 +0,0 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": ["github>privilegedescalation/.github:renovate-config"]
}