Compare commits

...

5 Commits

13 changed files with 317 additions and 53 deletions
+1 -1
View File
@@ -14,4 +14,4 @@ jobs:
- name: Draft Release - name: Draft Release
uses: release-drafter/release-drafter@b1476f6e6eb133afa41ed8589daba6dc69b4d3f5 # v6 uses: release-drafter/release-drafter@b1476f6e6eb133afa41ed8589daba6dc69b4d3f5 # v6
env: env:
CHARON_TOKEN: ${{ secrets.CHARON_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+9 -7
View File
@@ -23,10 +23,12 @@ jobs:
with: with:
# The prefix to use to create tags # The prefix to use to create tags
tag_prefix: "v" tag_prefix: "v"
# A string which, if present in the git log, indicates that a major version increase is required # Regex pattern for major version bump (breaking changes)
major_pattern: "(MAJOR)" # Matches: "feat!:", "fix!:", "BREAKING CHANGE:" in commit messages
# A string which, if present in the git log, indicates that a minor version increase is required major_pattern: "/!:|BREAKING CHANGE:/"
minor_pattern: "(feat)" # Regex pattern for minor version bump (new features)
# Matches: "feat:" prefix in commit messages (Conventional Commits)
minor_pattern: "/feat:/"
# Pattern to determine formatting # Pattern to determine formatting
version_format: "${major}.${minor}.${patch}" version_format: "${major}.${minor}.${patch}"
# If no tags are found, this version is used # If no tags are found, this version is used
@@ -66,7 +68,7 @@ jobs:
# Export the tag for downstream steps # Export the tag for downstream steps
echo "tag=${TAG}" >> $GITHUB_OUTPUT echo "tag=${TAG}" >> $GITHUB_OUTPUT
env: env:
CHARON_TOKEN: ${{ secrets.CHARON_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Determine tag - name: Determine tag
id: determine_tag id: determine_tag
@@ -87,14 +89,14 @@ jobs:
run: | run: |
TAG=${{ steps.determine_tag.outputs.tag }} TAG=${{ steps.determine_tag.outputs.tag }}
echo "Checking for release for tag: ${TAG}" echo "Checking for release for tag: ${TAG}"
STATUS=$(curl -s -o /dev/null -w "%{http_code}" -H "Authorization: token ${CHARON_TOKEN}" -H "Accept: application/vnd.github+json" "https://api.github.com/repos/${GITHUB_REPOSITORY}/releases/tags/${TAG}") || true STATUS=$(curl -s -o /dev/null -w "%{http_code}" -H "Authorization: token ${GITHUB_TOKEN}" -H "Accept: application/vnd.github+json" "https://api.github.com/repos/${GITHUB_REPOSITORY}/releases/tags/${TAG}") || true
if [ "${STATUS}" = "200" ]; then if [ "${STATUS}" = "200" ]; then
echo "exists=true" >> $GITHUB_OUTPUT echo "exists=true" >> $GITHUB_OUTPUT
else else
echo "exists=false" >> $GITHUB_OUTPUT echo "exists=false" >> $GITHUB_OUTPUT
fi fi
env: env:
CHARON_TOKEN: ${{ secrets.CHARON_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Create GitHub Release (tag-only, no workspace changes) - name: Create GitHub Release (tag-only, no workspace changes)
if: ${{ steps.semver.outputs.changed == 'true' && steps.check_release.outputs.exists == 'false' }} if: ${{ steps.semver.outputs.changed == 'true' && steps.check_release.outputs.exists == 'false' }}
+1 -1
View File
@@ -157,5 +157,5 @@ jobs:
} }
} }
env: env:
CHARON_TOKEN: ${{ secrets.CHARON_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CPMP_TOKEN: ${{ secrets.CPMP_TOKEN }} CPMP_TOKEN: ${{ secrets.CPMP_TOKEN }}
+8 -6
View File
@@ -13,10 +13,10 @@ jobs:
goreleaser: goreleaser:
runs-on: ubuntu-latest runs-on: ubuntu-latest
env: env:
# Use the built-in CHARON_TOKEN by default for GitHub API operations. # Use the built-in GITHUB_TOKEN by default for GitHub API operations.
# If you need to provide a PAT with elevated permissions, add a CHARON_TOKEN secret # If you need to provide a PAT with elevated permissions, add a GITHUB_TOKEN secret
# at the repo or organization level and update the env here accordingly. # at the repo or organization level and update the env here accordingly.
CHARON_TOKEN: ${{ secrets.CHARON_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6 uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
@@ -26,12 +26,12 @@ jobs:
- name: Set up Go - name: Set up Go
uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6 uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6
with: with:
go-version: '1.25.5' go-version: '1.23.x'
- name: Set up Node.js - name: Set up Node.js
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6 uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6
with: with:
node-version: '24.12.0' node-version: '20.x'
- name: Build Frontend - name: Build Frontend
working-directory: frontend working-directory: frontend
@@ -47,7 +47,7 @@ jobs:
with: with:
version: 0.13.0 version: 0.13.0
# CHARON_TOKEN is set from CHARON_TOKEN or CPMP_TOKEN (fallback), defaulting to GITHUB_TOKEN # GITHUB_TOKEN is set from GITHUB_TOKEN or CPMP_TOKEN (fallback), defaulting to GITHUB_TOKEN
- name: Run GoReleaser - name: Run GoReleaser
@@ -56,4 +56,6 @@ jobs:
distribution: goreleaser distribution: goreleaser
version: latest version: latest
args: release --clean args: release --clean
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# CGO settings are handled in .goreleaser.yaml via Zig # CGO settings are handled in .goreleaser.yaml via Zig
+2 -19
View File
@@ -2,7 +2,7 @@ name: Renovate
on: on:
schedule: schedule:
- cron: '0 5 * * *' # daily 05:00 EST - cron: '0 5 * * *' # daily 05:00 UTC
workflow_dispatch: workflow_dispatch:
permissions: permissions:
@@ -18,28 +18,11 @@ jobs:
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6 uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
with: with:
fetch-depth: 1 fetch-depth: 1
- name: Choose Renovate Token
run: |
# Prefer explicit tokens (CHARON_TOKEN > CPMP_TOKEN) if provided; otherwise use the default GITHUB_TOKEN
if [ -n "${{ secrets.CHARON_TOKEN }}" ]; then
echo "Using CHARON_TOKEN" >&2
echo "GITHUB_TOKEN=${{ secrets.CHARON_TOKEN }}" >> $GITHUB_ENV
else
echo "Using default GITHUB_TOKEN from Actions" >&2
echo "GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }}" >> $GITHUB_ENV
fi
- name: Fail-fast if token not set
run: |
if [ -z "${{ env.GITHUB_TOKEN }}" ]; then
echo "ERROR: No Renovate token provided. Set CHARON_TOKEN, CPMP_TOKEN, or rely on default GITHUB_TOKEN." >&2
exit 1
fi
- name: Run Renovate - name: Run Renovate
uses: renovatebot/github-action@502904f1cefdd70cba026cb1cbd8c53a1443e91b # v44.1.0 uses: renovatebot/github-action@502904f1cefdd70cba026cb1cbd8c53a1443e91b # v44.1.0
with: with:
configurationFile: .github/renovate.json configurationFile: .github/renovate.json
token: ${{ env.GITHUB_TOKEN }} token: ${{ secrets.GITHUB_TOKEN }}
env: env:
LOG_LEVEL: info LOG_LEVEL: info
+5 -5
View File
@@ -24,17 +24,17 @@ jobs:
steps: steps:
- name: Choose GitHub Token - name: Choose GitHub Token
run: | run: |
if [ -n "${{ secrets.CHARON_TOKEN }}" ]; then if [ -n "${{ secrets.GITHUB_TOKEN }}" ]; then
echo "Using CHARON_TOKEN" >&2 echo "Using GITHUB_TOKEN" >&2
echo "CHARON_TOKEN=${{ secrets.CHARON_TOKEN }}" >> $GITHUB_ENV echo "GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }}" >> $GITHUB_ENV
else else
echo "Using CPMP_TOKEN fallback" >&2 echo "Using CPMP_TOKEN fallback" >&2
echo "CHARON_TOKEN=${{ secrets.CPMP_TOKEN }}" >> $GITHUB_ENV echo "GITHUB_TOKEN=${{ secrets.CPMP_TOKEN }}" >> $GITHUB_ENV
fi fi
- name: Prune renovate branches - name: Prune renovate branches
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with: with:
github-token: ${{ env.CHARON_TOKEN }} github-token: ${{ env.GITHUB_TOKEN }}
script: | script: |
const owner = context.repo.owner; const owner = context.repo.owner;
const repo = context.repo.repo; const repo = context.repo.repo;
+5 -2
View File
@@ -81,8 +81,11 @@ charon.db
*~ *~
.DS_Store .DS_Store
*.xcf *.xcf
.vscode/ # VS Code - ignore settings but keep shared configs
.vscode/launch.json .vscode/*
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
.vscode.backup*/ .vscode.backup*/
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
+22
View File
@@ -0,0 +1,22 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "Attach to Backend (Docker)",
"type": "go",
"request": "attach",
"mode": "remote",
"substitutePath": [
{
"from": "${workspaceFolder}",
"to": "/app"
}
],
"port": 2345,
"host": "127.0.0.1",
"showLog": true,
"trace": "log",
"logOutput": "rpc"
}
]
}
+252
View File
@@ -0,0 +1,252 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "Build: Local Docker Image",
"type": "shell",
"command": "docker build -t charon:local .",
"group": "build",
"problemMatcher": [],
"presentation": {
"reveal": "always",
"panel": "new"
}
},
{
"label": "Build: Backend",
"type": "shell",
"command": "cd backend && go build ./...",
"group": "build",
"problemMatcher": ["$go"]
},
{
"label": "Build: Frontend",
"type": "shell",
"command": "cd frontend && npm run build",
"group": "build",
"problemMatcher": []
},
{
"label": "Build: All",
"type": "shell",
"dependsOn": ["Build: Backend", "Build: Frontend"],
"group": {
"kind": "build",
"isDefault": true
},
"problemMatcher": []
},
{
"label": "Test: Backend Unit Tests",
"type": "shell",
"command": "cd backend && go test ./...",
"group": "test",
"problemMatcher": ["$go"]
},
{
"label": "Test: Backend with Coverage",
"type": "shell",
"command": "scripts/go-test-coverage.sh",
"group": "test",
"problemMatcher": []
},
{
"label": "Test: Frontend",
"type": "shell",
"command": "cd frontend && npm run test",
"group": "test",
"problemMatcher": []
},
{
"label": "Test: Frontend with Coverage",
"type": "shell",
"command": "scripts/frontend-test-coverage.sh",
"group": "test",
"problemMatcher": []
},
{
"label": "Lint: Pre-commit (All Files)",
"type": "shell",
"command": "source .venv/bin/activate && pre-commit run --all-files",
"group": "test",
"problemMatcher": [],
"presentation": {
"reveal": "always",
"panel": "shared"
}
},
{
"label": "Lint: Go Vet",
"type": "shell",
"command": "cd backend && go vet ./...",
"group": "test",
"problemMatcher": ["$go"]
},
{
"label": "Lint: GolangCI-Lint (Docker)",
"type": "shell",
"command": "cd backend && docker run --rm -v $(pwd):/app:ro -w /app golangci/golangci-lint:latest golangci-lint run -v",
"group": "test",
"problemMatcher": []
},
{
"label": "Lint: Frontend",
"type": "shell",
"command": "cd frontend && npm run lint",
"group": "test",
"problemMatcher": []
},
{
"label": "Lint: Frontend (Fix)",
"type": "shell",
"command": "cd frontend && npm run lint -- --fix",
"group": "test",
"problemMatcher": []
},
{
"label": "Lint: TypeScript Check",
"type": "shell",
"command": "cd frontend && npm run type-check",
"group": "test",
"problemMatcher": []
},
{
"label": "Lint: Markdownlint",
"type": "shell",
"command": "npx markdownlint '**/*.md' --ignore node_modules --ignore .venv --ignore test-results --ignore codeql-db --ignore codeql-agent-results",
"group": "test",
"problemMatcher": []
},
{
"label": "Lint: Markdownlint (Fix)",
"type": "shell",
"command": "npx markdownlint '**/*.md' --fix --ignore node_modules --ignore .venv --ignore test-results --ignore codeql-db --ignore codeql-agent-results",
"group": "test",
"problemMatcher": []
},
{
"label": "Lint: Hadolint Dockerfile",
"type": "shell",
"command": "docker run --rm -i hadolint/hadolint < Dockerfile",
"group": "test",
"problemMatcher": []
},
{
"label": "Security: Trivy Scan",
"type": "shell",
"command": "docker run --rm -v $(pwd):/app aquasec/trivy:latest fs --scanners vuln,secret,misconfig /app",
"group": "test",
"problemMatcher": []
},
{
"label": "Security: Go Vulnerability Check",
"type": "shell",
"command": "cd backend && go run golang.org/x/vuln/cmd/govulncheck@latest ./...",
"group": "test",
"problemMatcher": []
},
{
"label": "Docker: Start Dev Environment",
"type": "shell",
"command": "docker compose -f docker-compose.dev.yml up -d",
"group": "none",
"problemMatcher": []
},
{
"label": "Docker: Stop Dev Environment",
"type": "shell",
"command": "docker compose -f docker-compose.dev.yml down",
"group": "none",
"problemMatcher": []
},
{
"label": "Docker: Start Local Environment",
"type": "shell",
"command": "docker compose -f docker-compose.local.yml up -d",
"group": "none",
"problemMatcher": []
},
{
"label": "Docker: Stop Local Environment",
"type": "shell",
"command": "docker compose -f docker-compose.local.yml down",
"group": "none",
"problemMatcher": []
},
{
"label": "Docker: View Logs",
"type": "shell",
"command": "docker compose logs -f",
"group": "none",
"problemMatcher": [],
"isBackground": true
},
{
"label": "Docker: Prune Unused Resources",
"type": "shell",
"command": "docker system prune -f",
"group": "none",
"problemMatcher": []
},
{
"label": "Integration: Run All",
"type": "shell",
"command": "scripts/integration-test.sh",
"group": "test",
"problemMatcher": [],
"presentation": {
"reveal": "always",
"panel": "new"
}
},
{
"label": "Integration: Coraza WAF",
"type": "shell",
"command": "scripts/coraza_integration.sh",
"group": "test",
"problemMatcher": []
},
{
"label": "Integration: CrowdSec",
"type": "shell",
"command": "scripts/crowdsec_integration.sh",
"group": "test",
"problemMatcher": []
},
{
"label": "Integration: CrowdSec Decisions",
"type": "shell",
"command": "scripts/crowdsec_decision_integration.sh",
"group": "test",
"problemMatcher": []
},
{
"label": "Integration: CrowdSec Startup",
"type": "shell",
"command": "scripts/crowdsec_startup_test.sh",
"group": "test",
"problemMatcher": []
},
{
"label": "Utility: Check Version Match Tag",
"type": "shell",
"command": "scripts/check-version-match-tag.sh",
"group": "none",
"problemMatcher": []
},
{
"label": "Utility: Clear Go Cache",
"type": "shell",
"command": "scripts/clear-go-cache.sh",
"group": "none",
"problemMatcher": []
},
{
"label": "Utility: Bump Beta Version",
"type": "shell",
"command": "scripts/bump_beta.sh",
"group": "none",
"problemMatcher": []
}
]
}
+2 -2
View File
@@ -7,7 +7,7 @@ This draft PR merges recent beta preparation changes from `feature/beta-release`
## Changes Included ## Changes Included
1. Workflow Token Updates 1. Workflow Token Updates
- Prefer `CHARON_TOKEN` with `CPMP_TOKEN` as a fallback to maintain backward compatibility. - Prefer `GITHUB_TOKEN` with `CPMP_TOKEN` as a fallback to maintain backward compatibility.
- Ensured consistent secret reference across `release.yml` and `renovate_prune.yml`. - Ensured consistent secret reference across `release.yml` and `renovate_prune.yml`.
2. Release Workflow Adjustments 2. Release Workflow Adjustments
- Fixed environment variable configuration for release publication. - Fixed environment variable configuration for release publication.
@@ -68,7 +68,7 @@ This draft PR merges recent beta preparation changes from `feature/beta-release`
Marking this as a DRAFT to allow review of token changes before merge. Please: Marking this as a DRAFT to allow review of token changes before merge. Please:
- Confirm `CHARON_TOKEN` (or `CPMP_TOKEN` fallback) exists in repo secrets. - Confirm `GITHUB_TOKEN` (or `CPMP_TOKEN` fallback) exists in repo secrets.
- Review for any missed workflow references. - Review for any missed workflow references.
--- ---
+4 -4
View File
@@ -6,7 +6,7 @@ This draft PR merges recent beta preparation changes from `feature/beta-release`
## Changes Included (Summary) ## Changes Included (Summary)
- Workflow token migration: prefer `CHARON_TOKEN` (fallback `CPMP_TOKEN`) across release and maintenance workflows. - Workflow token migration: prefer `GITHUB_TOKEN` (fallback `CPMP_TOKEN`) across release and maintenance workflows.
- Stabilized release workflow prerelease detection and artifact publication. - Stabilized release workflow prerelease detection and artifact publication.
- Prior (already merged earlier) CI enhancements: pinned action versions, Docker multi-arch debug tooling reliability, dynamic `dlv` binary resolution. - Prior (already merged earlier) CI enhancements: pinned action versions, Docker multi-arch debug tooling reliability, dynamic `dlv` binary resolution.
- Documentation updates enumerating each incremental workflow/token adjustment for auditability. - Documentation updates enumerating each incremental workflow/token adjustment for auditability.
@@ -21,7 +21,7 @@ Ensures alpha integration branch inherits hardened CI/release pipeline and updat
## Risk & Mitigation ## Risk & Mitigation
- Secret Name Change: Prefer `CHARON_TOKEN` (keep `CPMP_TOKEN` as a fallback). Mitigation: Verify `CHARON_TOKEN` (or `CPMP_TOKEN`) presence before merge. - Secret Name Change: Prefer `GITHUB_TOKEN` (keep `CPMP_TOKEN` as a fallback). Mitigation: Verify `GITHUB_TOKEN` (or `CPMP_TOKEN`) presence before merge.
- Workflow Fan-out: Reusable workflow path validated locally; CI run (draft) will confirm. - Workflow Fan-out: Reusable workflow path validated locally; CI run (draft) will confirm.
## Follow-ups (Out of Scope) ## Follow-ups (Out of Scope)
@@ -38,9 +38,9 @@ Ensures alpha integration branch inherits hardened CI/release pipeline and updat
## Requested Review Focus ## Requested Review Focus
1. Confirm `CHARON_TOKEN` (or `CPMP_TOKEN` fallback) availability. 1. Confirm `GITHUB_TOKEN` (or `CPMP_TOKEN` fallback) availability.
2. Sanity-check release artifact matrix remains correct. 2. Sanity-check release artifact matrix remains correct.
3. Spot any residual `CHARON_TOKEN` or `CPMP_TOKEN` references missed. 3. Spot any residual `GITHUB_TOKEN` or `CPMP_TOKEN` references missed.
--- ---
Generated draft to align branches; will convert to ready-for-review after validation. Generated draft to align branches; will convert to ready-for-review after validation.
+3 -3
View File
@@ -6,7 +6,7 @@ Draft PR to merge hardened CI/release workflow changes from `feature/beta-releas
## Highlights ## Highlights
- Secret token migration: prefer `CHARON_TOKEN` while maintaining support for `CPMP_TOKEN` (fallback) where needed. - Secret token migration: prefer `GITHUB_TOKEN` while maintaining support for `CPMP_TOKEN` (fallback) where needed.
- Release workflow refinements: stable prerelease detection (alpha/beta/rc), artifact matrix intact. - Release workflow refinements: stable prerelease detection (alpha/beta/rc), artifact matrix intact.
- Prior infra hardening (already partially merged earlier): pinned GitHub Action SHAs/tags, resilient Delve (`dlv`) multi-arch build handling. - Prior infra hardening (already partially merged earlier): pinned GitHub Action SHAs/tags, resilient Delve (`dlv`) multi-arch build handling.
- Extensive incremental documentation trail in `docs/beta_release_draft_pr.md` plus concise snapshot in `docs/beta_release_draft_pr_body_snapshot.md` for reviewers. - Extensive incremental documentation trail in `docs/beta_release_draft_pr.md` plus concise snapshot in `docs/beta_release_draft_pr_body_snapshot.md` for reviewers.
@@ -17,8 +17,8 @@ Most recent snapshot commit: `308ae5dd` (final body content before PR). Full ord
## Review Checklist ## Review Checklist
- Secret `CHARON_TOKEN` (or `CPMP_TOKEN` fallback) exists and has required scopes. - Secret `GITHUB_TOKEN` (or `CPMP_TOKEN` fallback) exists and has required scopes.
- No lingering `CHARON_TOKEN` or `CPMP_TOKEN` references beyond allowed GitHub-provided contexts. - No lingering `GITHUB_TOKEN` or `CPMP_TOKEN` references beyond allowed GitHub-provided contexts.
- Artifact list (frontend dist, backend binaries, caddy binaries) still correct for release. - Artifact list (frontend dist, backend binaries, caddy binaries) still correct for release.
## Risks & Mitigations ## Risks & Mitigations
+3 -3
View File
@@ -10,7 +10,7 @@ The Docker build workflow uses GitHub Container Registry (GHCR) to store your im
### How It Works ### How It Works
GitHub Actions automatically uses the built-in secret token to authenticate with GHCR. We recommend creating a `CHARON_TOKEN` secret (preferred); workflows currently still work with `CPMP_TOKEN` for backward compatibility. GitHub Actions automatically uses the built-in secret token to authenticate with GHCR. We recommend creating a `GITHUB_TOKEN` secret (preferred); workflows currently still work with `CPMP_TOKEN` for backward compatibility.
- ✅ Push images to `ghcr.io/wikid82/charon` - ✅ Push images to `ghcr.io/wikid82/charon`
- ✅ Link images to your repository - ✅ Link images to your repository
@@ -172,13 +172,13 @@ When you're ready to release a new version:
**Problem**: "Error: denied: requested access to the resource is denied" **Problem**: "Error: denied: requested access to the resource is denied"
- **Fix**: This shouldn't happen with `CHARON_TOKEN` or `CPMP_TOKEN` - check workflow permissions - **Fix**: This shouldn't happen with `GITHUB_TOKEN` or `CPMP_TOKEN` - check workflow permissions
- **Verify**: Settings → Actions → General → Workflow permissions → "Read and write permissions" enabled - **Verify**: Settings → Actions → General → Workflow permissions → "Read and write permissions" enabled
**Problem**: Can't pull the image **Problem**: Can't pull the image
- **Fix**: Make the package public (see Step 1 above) - **Fix**: Make the package public (see Step 1 above)
- **Or**: Authenticate with GitHub: `echo $CHARON_TOKEN | docker login ghcr.io -u USERNAME --password-stdin` (or `CPMP_TOKEN` for backward compatibility) - **Or**: Authenticate with GitHub: `echo $GITHUB_TOKEN | docker login ghcr.io -u USERNAME --password-stdin` (or `CPMP_TOKEN` for backward compatibility)
### Docs Don't Deploy ### Docs Don't Deploy