diff --git a/.github/scripts/ci-health-check.sh b/.github/scripts/ci-health-check.sh index bdc9a17..ba8e19d 100755 --- a/.github/scripts/ci-health-check.sh +++ b/.github/scripts/ci-health-check.sh @@ -129,4 +129,4 @@ echo "With pending approval: ${process_pending}" if [ "$failures" -gt 0 ]; then exit 1 -fi \ No newline at end of file +fi diff --git a/.github/workflows/ci-health-check.yaml b/.github/workflows/ci-health-check.yaml index c3deaea..fa180e5 100644 --- a/.github/workflows/ci-health-check.yaml +++ b/.github/workflows/ci-health-check.yaml @@ -12,22 +12,11 @@ jobs: - name: Checkout repository uses: actions/checkout@v6 - - name: Generate GitHub App token - id: app-token - uses: actions/create-github-app-token@v3 - continue-on-error: true - with: - app-id: ${{ secrets.RELEASE_APP_ID }} - private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }} - owner: privilegedescalation - - name: Run CI/CD health check env: - GH_TOKEN: ${{ steps.app-token.outputs.token || secrets.GITHUB_TOKEN }} + GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }} run: | - if [ "${{ steps.app-token.outcome }}" = "success" ]; then - echo "Using GitHub App token for cross-repo access" - else - echo "::warning::RELEASE_APP_ID not configured — using GITHUB_TOKEN. Cross-repo workflow run data will be unavailable. Configure RELEASE_APP_ID org secret to enable full health check." + if [ -z "$GITEA_TOKEN" ]; then + echo "::warning::GITEA_TOKEN not configured — health check may have limited data." fi - ./.github/scripts/ci-health-check.sh + ./.github/scripts/ci-health-check.sh \ No newline at end of file diff --git a/.github/workflows/plugin-release.yaml b/.github/workflows/plugin-release.yaml index 2372925..aa2bd8d 100644 --- a/.github/workflows/plugin-release.yaml +++ b/.github/workflows/plugin-release.yaml @@ -18,11 +18,8 @@ on: type: string default: '' secrets: - RELEASE_APP_ID: - description: 'GitHub App ID for creating PRs (org blocks GITHUB_TOKEN from creating PRs)' - required: true - RELEASE_APP_PRIVATE_KEY: - description: 'GitHub App private key (PEM format)' + GITEA_RELEASE_TOKEN: + description: 'Gitea token with write access to repos (replaces GitHub App token flow)' required: true permissions: @@ -39,13 +36,13 @@ jobs: outputs: ready: ${{ steps.check.outputs.ready }} steps: - - name: Verify RELEASE_APP_ID is configured + - name: Verify GITEA_RELEASE_TOKEN is configured id: check env: - RELEASE_APP_ID: ${{ secrets.RELEASE_APP_ID }} + GITEA_RELEASE_TOKEN: ${{ secrets.GITEA_TOKEN }} run: | - if [ -z "$RELEASE_APP_ID" ]; then - echo "::notice::RELEASE_APP_ID org secret is not configured (see PRI-380). Release skipped — no artifacts will be created." + 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 @@ -65,28 +62,24 @@ jobs: outputs: has_write: ${{ steps.check.outputs.has_write }} steps: - - name: Generate GitHub App token - id: app-token - uses: actions/create-github-app-token@v3 - with: - app-id: ${{ secrets.RELEASE_APP_ID }} - private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }} - - name: Check write permissions via API id: check + env: + GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }} + REPO: ${{ github.repository }} run: | - HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" \ + HTTP_CODE=$(curl -sf -o /dev/null -w "%{http_code}" \ -X POST \ - -H "Authorization: Bearer ${{ steps.app-token.outputs.token }}" \ - -H "Accept: application/vnd.github+json" \ - "https://api.github.com/repos/${{ github.repository }}/git/refs" \ + -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 -s -o /dev/null -w "%{http_code}" \ + curl -sf -o /dev/null -w "%{http_code}" \ -X DELETE \ - -H "Authorization: Bearer ${{ steps.app-token.outputs.token }}" \ - "https://api.github.com/repos/${{ github.repository }}/git/refs/heads/_release_check" + -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." @@ -107,10 +100,13 @@ jobs: steps: - name: Check if tag already exists id: check + env: + GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }} + REPO: ${{ github.repository }} run: | - HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" \ - -H "Authorization: Bearer ${{ github.token }}" \ - "https://api.github.com/repos/${{ github.repository }}/git/refs/tags/v${{ inputs.version }}") + 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 @@ -143,10 +139,6 @@ jobs: if [ -f "pnpm-lock.yaml" ]; then echo "manager=pnpm" >> $GITHUB_OUTPUT echo "lockfile=pnpm-lock.yaml" >> $GITHUB_OUTPUT - # Check for packageManager field in package.json (Corepack pinning). - # pnpm/action-setup@v5 errors when packageManager is absent and no version - # is specified, so use Corepack for repos that have the field pinned and - # fall back to pnpm/action-setup with version: latest for repos that don't. 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 @@ -159,11 +151,9 @@ jobs: uses: actions/setup-node@v6 with: node-version: ${{ inputs.node-version }} - # Only enable built-in npm caching here; pnpm caching is handled below - # after pnpm is installed (corepack is not available before setup-node). cache: ${{ steps.pkg-manager.outputs.manager == 'npm' && 'npm' || '' }} - - name: Setup pnpm (via Corepack, reads version from packageManager field) + - 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 @@ -192,10 +182,13 @@ jobs: ${{ runner.os }}-pnpm- - name: Configure Git + env: + GITEA_TOKEN: ${{ secrets.GITEA_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: | @@ -206,6 +199,8 @@ jobs: fi - name: Update artifacthub-pkg.yml + env: + REPO: ${{ github.repository }} run: | VERSION="${{ inputs.version }}" if [ -f artifacthub-pkg.yml ]; then @@ -213,14 +208,18 @@ jobs: else PKG_NAME=$(jq -r .name package.json | sed 's|^@[^/]*/||') fi - RELEASE_URL="https://github.com/${{ github.repository }}/releases/download/v${VERSION}/${PKG_NAME}-${VERSION}.tar.gz" + 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 if: inputs.upstream-repo != '' + env: + GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }} run: | - APP_VERSION=$(curl -sf "https://api.github.com/repos/${{ inputs.upstream-repo }}/releases/latest" | jq -r '.tag_name | ltrimstr("v")') + APP_VERSION=$(curl -sf \ + -H "Authorization: token ${GITEA_TOKEN}" \ + "https://git.farh.net/api/v1/repos/${{ inputs.upstream-repo }}/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 @@ -259,8 +258,6 @@ jobs: - name: Prepare release tarball run: | VERSION="${{ inputs.version }}" - # headlamp-plugin strips the @org/ prefix when naming tarballs. - # e.g. @privilegedescalation/headlamp-argocd-plugin -> headlamp-argocd-plugin if [ -f artifacthub-pkg.yml ]; then PKG_NAME=$(grep '^name:' artifacthub-pkg.yml | cut -d: -f2 | tr -d ' "') else @@ -292,13 +289,11 @@ jobs: 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_TOKEN }} run: | VERSION="${{ inputs.version }}" BRANCH="release/v${VERSION}" - # If the release branch already exists (e.g. from a failed prior run), - # delete it so the re-trigger can proceed cleanly. The check-tag job - # above already skips when the tag exists, so we only reach here when - # the tag does NOT exist yet — safe to remove a stale branch. 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" @@ -310,131 +305,113 @@ jobs: git push origin "$BRANCH" git push origin "refs/tags/v${VERSION}" - - name: Create GitHub Release - uses: softprops/action-gh-release@v2 - with: - tag_name: "v${{ inputs.version }}" - files: ${{ env.TARBALL }} - fail_on_unmatched_files: false - generate_release_notes: true + - name: Create Gitea Release env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Generate GitHub App token - id: app-token - uses: actions/create-github-app-token@v3 - with: - app-id: ${{ secrets.RELEASE_APP_ID }} - private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }} - - - name: Install GitHub CLI + GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }} + REPO: ${{ github.repository }} run: | - if ! command -v gh &>/dev/null; then - GH_VERSION="2.74.0" - curl -fsSL "https://github.com/cli/cli/releases/download/v${GH_VERSION}/gh_${GH_VERSION}_linux_amd64.tar.gz" -o /tmp/gh.tar.gz - tar -xzf /tmp/gh.tar.gz -C /tmp - mkdir -p "$HOME/.local/bin" - mv "/tmp/gh_${GH_VERSION}_linux_amd64/bin/gh" "$HOME/.local/bin/gh" - rm -rf /tmp/gh.tar.gz "/tmp/gh_${GH_VERSION}_linux_amd64" - echo "$HOME/.local/bin" >> "$GITHUB_PATH" - "$HOME/.local/bin/gh" --version + 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_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}") - # Create PR only if an OPEN one doesn't already exist. - # Note: gh pr view also finds MERGED PRs; we must check for open ones explicitly - # so that a re-trigger after a stale-branch delete creates a fresh PR. - OPEN_PR=$(gh pr list --base main --head "release/v${VERSION}" --state open --json number --jq '.[0].number' 2>/dev/null) - if [ -z "$OPEN_PR" ]; then - gh pr create \ - --title "release: v${VERSION}" \ - --body "$BODY" \ - --base main \ - --head "release/v${VERSION}" - # Pull the number again to handle both create and pre-existing cases - OPEN_PR=$(gh pr list --base main --head "release/v${VERSION}" --state open --json number --jq '.[0].number' 2>/dev/null) - else - echo "::notice::Open PR #${OPEN_PR} for release/v${VERSION} already exists — skipping creation." + 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 - # Guard: ensure we have a PR number before proceeding - if [ -z "$OPEN_PR" ]; then - echo "::error::Could not determine PR number for release/v${VERSION}." - exit 1 - fi - echo "::notice::Working with PR #${OPEN_PR}" + echo "::notice::Working with PR #${PR_NUMBER}" - # Check if PR was already merged (idempotency — safe to re-trigger after a stale branch) - MERGED_CHECK=$(gh pr view "$OPEN_PR" --json state --jq '.state' 2>/dev/null) - if [ "$MERGED_CHECK" = "MERGED" ]; then - echo "::notice::PR #${OPEN_PR} was already merged. Nothing to do." + 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 - # Determine whether to use --auto or not based on current status. - # Retry the status check up to 3 times with exponential back-off when - # GitHub is still computing the merge state (UNKNOWN state). - MAX_RETRIES=3 - BACKOFF=3 - MERGE_STATE="" - for i in $(seq 1 $MAX_RETRIES); do - MERGE_STATE=$(gh pr view "$OPEN_PR" --json mergeStateStatus --jq '.mergeStateStatus' 2>/dev/null) - if [ "$MERGE_STATE" != "UNKNOWN" ]; then - break - fi - if [ $i -lt $MAX_RETRIES ]; then - echo "PR merge state is UNKNOWN (GitHub still computing). Retry ${i}/${MAX_RETRIES} in ${BACKOFF}s..." - sleep $BACKOFF - BACKOFF=$((BACKOFF * 2)) - fi - done - if [ "$MERGE_STATE" = "BLOCKED" ] || [ "$MERGE_STATE" = "UNKNOWN" ]; then - echo "PR is $MERGE_STATE — attempting auto-merge (safe fallback, waits for branch protection checks)." - if gh pr merge "$OPEN_PR" --auto --squash --delete-branch 2>&1; then - echo "Auto-merge initiated successfully." - else - AUTO_MERGE_ERR=$? - # If --auto failed because auto-merge is disabled for this repo - # (autoMergeAllowed: false), fall back to --admin which merges - # regardless of branch protection rules. --admin requires GitHub - # App token, not GITHUB_TOKEN, so GH_TOKEN is already correct. - if gh pr merge "$OPEN_PR" --admin --squash --delete-branch 2>&1; then - echo "Auto-merge unavailable (autoMergeAllowed: false) — merged via --admin." - else - echo "::error::Both --auto and --admin merge failed. Exiting." - exit 1 - fi - 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 - echo "PR is $MERGE_STATE — merging directly." - gh pr merge "$OPEN_PR" --squash --delete-branch + if [ "$PR_STATE" = "merged" ]; then + echo "PR was already merged." + else + echo "::warning::Merge response: $MERGE_RESULT" + fi fi - env: - GH_TOKEN: ${{ steps.app-token.outputs.token }} - name: Verify checksums are consistent (main == tag == tarball) + env: + GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }} + REPO: ${{ github.repository }} run: | VERSION="${{ inputs.version }}" TARBALL_CS=$(sha256sum "${{ env.TARBALL }}" | awk '{print $1}') - - # Checksum recorded in the tag's artifacthub-pkg.yml - TAG_CS=$(git show "v${VERSION}:artifacthub-pkg.yml" 2>/dev/null | grep "archive-checksum" | awk '{print $2}' | sed 's/sha256://') - - # Checksum now on main (after PR merge) - 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://') - + 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." - env: - GH_TOKEN: ${{ steps.app-token.outputs.token }} - + echo "All checksums consistent — ArtifactHub will index correctly." \ No newline at end of file diff --git a/.github/workflows/renovate.yaml b/.github/workflows/renovate.yaml index 51001ec..0bd05d5 100644 --- a/.github/workflows/renovate.yaml +++ b/.github/workflows/renovate.yaml @@ -12,19 +12,15 @@ jobs: - name: Checkout repository uses: actions/checkout@v6 - - name: Generate GitHub App token - id: app-token - uses: actions/create-github-app-token@v3 - with: - app-id: ${{ secrets.RELEASE_APP_ID }} - private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }} - owner: privilegedescalation - - name: Run Renovate - uses: renovatebot/github-action@v41.0.0 - with: - token: ${{ steps.app-token.outputs.token }} - configurationFile: renovate-config.json env: - LOG_LEVEL: debug + RENOVATE_TOKEN: ${{ secrets.RENOVATE_TOKEN }} + RENOVATE_PLATFORM: gitea RENOVATE_AUTODISCOVER: "true" + LOG_LEVEL: debug + run: | + npx renovate \ + --token="${RENOVATE_TOKEN}" \ + --platform=gitea \ + --autodiscover=true \ + --configurationFile=renovate-config.json \ No newline at end of file