* chore(deps): bump actions/checkout from 6.0.3 to 7.0.0
Bumps [actions/checkout](https://github.com/actions/checkout) from 6.0.3 to 7.0.0.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](df4cb1c069...9c091bb21b)
---
updated-dependencies:
- dependency-name: actions/checkout
dependency-version: 7.0.0
dependency-type: direct:production
update-type: version-update:semver-major
...
Signed-off-by: dependabot[bot] <support@github.com>
* ci: set persist-credentials: false on read-only checkouts (zizmor artipacked)
Adds `persist-credentials: false` to the 9 checkout steps flagged by
zizmor's credential-persistence (artipacked) rule on PR #2292. All are
read-only CI/test/quality jobs that never use the git token afterward, so
not persisting it removes the leak surface. Checkouts that push (publish,
pr-autofix, commit-fork-prebuilds, etc.) keep credentials and are untouched.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Gergo Magyar <gergomagyar@icloud.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Collapse release-candidate.yml into publish.yml so there is exactly one workflow that publishes gitnexus to npm, creates GitHub Releases, and triggers Docker builds — for both release candidates and stable releases. Closes#1609 architecturally.
A first-stage `route` job classifies push-to-main / push-tag / workflow_dispatch into `rc` / `stable` modes and fails closed on malformed shapes. RC path runs rc-guard → ci.yml → publish (mint GitHub App token → checkout with persist-credentials:false → resolve next rc version → atomic v-tag + rc/<SHA> marker push → vtag integrity gate → npm publish via OIDC → GitHub prerelease → if: failure() cleanup) → docker.yml. Stable path verifies package.json matches the tag and publishes to `latest` via OIDC (no docker).
Hardening:
• Self-trigger prevention via negative-glob `tags: ['v*', '!v*-rc.*']` — the bug class behind #1609 cannot recur.
• Two distinct actions/checkout steps per mode (no conditional `token:` expression footgun).
• Workflow-level `permissions: {}` deny-all + per-job grants; `id-token: write` only where OIDC is used.
• npm Trusted Publishing replaces NPM_TOKEN (delete the secret after the first successful publish).
• GitHub App installation token (actions/create-github-app-token@v3.2.0) replaces the long-lived RELEASE_PUSH_TOKEN PAT (delete after first successful RC).
• vtag integrity gate fails closed on empty / mode-mismatched output (prevents Release named `main` from a github.ref fallback).
• Annotation-injection sanitization on every logged ref.
• Explicit `secrets:` passthrough on docker.yml (DOCKERHUB_USERNAME, DOCKERHUB_TOKEN); ci.yml no longer inherits anything.
• `if: failure()` cleanup auto-deletes v-tag + rc-marker on partial failure (eliminates the external-consumer phantom-version ingestion window).
• ACTIONS_STEP_DEBUG window closed via `set +x` wrap on the inline auth-header compute.
• Curated retry-loud error handling on `gh api` bot-user-id lookup and `npx semver`.
Pre-merge validation:
• 10-reviewer multi-agent code-review pass; 14 findings fixed inline (commit 820cefae), 6 deferred to follow-ups.
• End-to-end dry-run rehearsal via workflow_dispatch (run 25919563064) validated route classification, rc-guard, App token mint, RC checkout, version resolver, vtag synthetic-regex check, and faithful tarball pack at the bumped version.
• All zizmor findings on the unification commits closed.
• Branch-protection required checks all green.
Post-merge actions:
• After the first successful RC, delete the `NPM_TOKEN` and `RELEASE_PUSH_TOKEN` secrets — they are no longer used.
• The first real RC after merge is the live-fire test for steps dry-run could not exercise (atomic tag push, real npm OIDC handshake, GitHub Release creation, docker.yml under explicit secrets passthrough). The if: failure() cleanup step handles the partial-failure recovery automatically; the Rollback Runbook in CONTRIBUTING.md covers the rare cases auto-cleanup can't reach.
* Initial plan
* chore(security): harden workflow permissions and pin Docker base image digests
Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/2ddc8f2b-7355-48cf-9a0b-c06df66c3f47
* fix(security): restore permissions: {} on publish + release-candidate workflows
These two release-publishing workflows had permissions: {} (the strictest valid form) before PR #1454, which replaced it with permissions: read-all. Every job in both files already declares its own permissions block, so the workflow-level default is only the safety net for future jobs added without one — read-all weakens that net for no benefit. Restore {} and the explanatory comment.
Scorecard's TokenPermissions check accepts both forms, so this preserves U9 compliance.
* fix(security): narrow permissions: read-all to contents: read on 13 workflows
PR #1454 added permissions: read-all to 13 workflows that previously had no top-level permissions block. read-all is Scorecard-compliant but unnecessarily broad — every job in scope only needs contents:read at the workflow level (job-level blocks already grant the writes that any job actually performs).
Snapshot of every job in the 13 workflows confirms contents:read is sufficient:
- ci.yml: quality/tests/scope-parity have explicit contents:read job blocks; save-pr-meta uses upload-artifact only (no token scopes needed); ci-status is pure shell.
- ci-e2e.yml, ci-quality.yml, ci-scope-parity.yml, ci-tests.yml: all jobs do checkout + npm + tsc/vitest/playwright/upload-artifact only; no API token scopes required.
- claude.yml, codeql.yml, dependency-review.yml, docker.yml, gitleaks.yml, pr-labeler.yml, trivy.yml, workflow-lint.yml: all jobs already declare their own job-level blocks (security-events:write, pull-requests:write, packages:write, etc.) so the workflow-level default does not gate them.
zizmor (--min-severity high) is clean on the resulting tree. Pre-existing medium findings (secrets-inherit, artipacked) are in unrelated workflows and untouched by this commit.
scorecard.yml also uses read-all but pre-existed PR #1454 and is deferred to a follow-up PR per the plan's scope boundary.
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Gergő Magyar <gergomagyar@icloud.com>
The early Validate step ran on both workflow_call and push events, but
push events never populate inputs.tag (the tag comes from github.ref).
This regressed every real tag-push release — v1.6.3's Docker Build &
Push failed at that gate. The downstream Verify step already falls back
to GITHUB_REF, so the upfront guard only needs to cover workflow_call.
Wraps docker/build-push-action with a local composite action that retries
once on failure (upstream keeps retry out of the action per
docker/build-push-action#1422). Adds ignore-error=true on cache-to so GHA
cache export flakes don't fail an otherwise successful push.
- Emit `::notice::` in the resolve step when attempt 2 recovers from a
first-attempt failure, so silent retries are grep-able in run logs and
trending registry/cache flakes stay visible.
- Bind `retry-wait-seconds` via `env:` in the backoff step to match the
env-binding convention used elsewhere in docker.yml (TAG_INPUT, DIGEST,
TAGS) — no direct expression interpolation inside shell bodies.
Preserves existing contract end-to-end: SHA pin, provenance=max, sbom=true,
dual-registry push, `steps.build.outputs.digest` wiring to Cosign and the
build-provenance attestations.
* ci(docker): mirror signed images to Docker Hub alongside GHCR
docker.yml now publishes to docker.io/abhigyanpatwari/gitnexus{,-web} in
the same build step as the existing GHCR push, so both registries receive
the same digest, the same Cosign keyless signature, and the same SBOM /
build-provenance attestations. The Docker Hub login uses new repo secrets
DOCKERHUB_USERNAME / DOCKERHUB_TOKEN (scoped PAT, not account password).
Supply-chain guarantees carry over unchanged: the signing loop iterates
metadata-action's full tag set, so Docker Hub tags get signed at the
identical digest under the same docker.yml@refs/tags/v* identity. The
ClusterImagePolicy is extended with docker.io / index.docker.io / bare-
namespace globs so admission cannot be sidestepped by registry-prefix
choice. README and .env.example document both registries; RC section in
CONTRIBUTING.md notes the Docker Hub mirror tag.
Closes#1027
* ci(docker): publish to akonlabs Docker Hub namespace; add PR dry-run CI
- Hardcode `akonlabs` as the Docker Hub namespace in metadata-action and
both attestation subject-names (Docker Hub org differs from GitHub org
`abhigyanpatwari`, so `github.repository_owner` would produce the wrong ref)
- Update docs (.env.example, README, CONTRIBUTING) and the Kubernetes
ClusterImagePolicy globs to reference `akonlabs/gitnexus{,-web}`
- Add `pull_request` trigger so the image build runs as CI on every PR
(build only — no push, sign, or attestation)
- Add `workflow_dispatch` with `dry_run: boolean` (default true) for
manual build-only runs; all publish steps gated on
`github.event_name != 'pull_request' && !inputs.dry_run`
In a reusable workflow, github.event_name inherits the caller's
event (e.g. "push"), not "workflow_call". This caused the
type=raw tag to be disabled when docker.yml was called from
release-candidate.yml, producing no Docker tags at all and
failing the build.
Fix: check `inputs.tag != ''` instead, since inputs.tag is only
populated for workflow_call invocations.
Co-authored-by: wangjichao <wangjichao@inke.cn>
* Initial plan
* fix: guard docker job and add tag validation in docker.yml
- Add `&& needs.publish.outputs.vtag != ''` to the `docker` job's
`if:` in release-candidate.yml so it is skipped when publish
produces no vtag, preventing an opaque buildx "tag is needed" error.
- Add an early "Validate tag input" step in docker.yml that fails fast
with a clear ::error:: message when inputs.tag is empty, covering
direct workflow_call invocations that bypass the release-candidate
guard.
Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/b9afe2df-85ea-4a87-bf30-77f0e945a64d
Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com>
* fix: scope docker.yml tag validation to workflow_call only
Direct tag-push triggers (on: push, tags: v*) populate the tag from
GITHUB_REF and have inputs.tag empty, so the unconditional validation
step would fail every direct tag-push run. Restrict the new step to
workflow_call invocations, which is the only path where an empty tag
is actually a problem.
Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/4b7e3bfa-15c0-4186-affa-95cd71e50153
Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com>
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com>
* feat: add docker support
* feat: move docker files to root
* feat: add docker build and push workflow
* fix: pin docker action SHAs to verified commits
Made-with: Cursor
* fix: remove redundant --platform=$TARGETPLATFORM from runtime stage
Made-with: Cursor
* fix: upgrade docker actions to Node.js 24-compatible versions
Made-with: Cursor
* docs: updated readme
* fix: update docker references
* fix(docker-server): reject null bytes in resolvePath
Defensively harden the path traversal guard by returning null
early when the URL contains a null byte, before normalization runs.
Made-with: Cursor
* fix(docker-server): handle createReadStream errors
Attach an error listener before piping so mid-flight read errors
(truncated file, permission change) cleanly destroy the response
instead of being silently swallowed.
Made-with: Cursor
* fix(docker-server): replace existsSync with async stat
Eliminates the TOCTOU race between the initial stat call and the
subsequent existsSync check. Reuses the async stat pattern already
in place and removes the now-unused existsSync import.
Made-with: Cursor
* test(docker-server): add integration tests; fix %00 null-byte bypass
Decode the URL before the null-byte check so percent-encoded null
bytes (%00) are also rejected with 400 instead of falling through
to the SPA fallback. Adds 5 node:test integration tests covering
valid assets, SPA fallback, path traversal, null bytes, and 404.
Made-with: Cursor
* style: fix prettier formatting in docker-server files
Made-with: Cursor
* fix(docker): wire tests into CI, fix resolvePath separator, correct image namespace
- Add `node --test docker-server.test.mjs` step to ci-tests.yml so the
path-traversal guard tests run in every CI pass instead of being silently skipped.
- Fix resolvePath containment check: `startsWith(root)` would allow sibling
directories like `/app/dist-evil/`; now guards with `root + sep` or exact match.
- Update docker-compose.yaml default image from `abhigyanpatwari` namespace to
`brainifii` to match what docker.yml publishes to GHCR.
* fix(docker): update apt-get commands and set user permissions
- Modify Dockerfile and Dockerfile.test to include options for apt-get to bypass validity checks during updates.
- Set ownership of the /app directory to the 'node' user in the runtime stage for improved security and proper permission handling.
* fix(docker): switch to Alpine base images for smaller footprint
- Update Dockerfile to use Alpine-based Node.js images for both builder and runtime stages, reducing image size and improving performance.
- Replace apt-get commands with apk for package installation in the runtime stage.
* fix(docker): update Node.js version in Dockerfile
- Change base image from node:20-alpine to node:22-alpine
* fix(docker): update Node.js version in Dockerfile to 22-alpine for runtime
---------
Co-authored-by: kritik.b <kritik.b@media.net>