From 8778056d9e3eee5ce02a9147adf423a1f60e2a9d Mon Sep 17 00:00:00 2001 From: Nikhil Rajput Date: Mon, 29 Jun 2026 15:52:59 +0530 Subject: [PATCH 1/3] ci: add CI, release, and security workflows - Add CI (lint/typecheck/build/test via the repo's package manager), version-check (enforce a package.json bump per PR), and release (tag + GitHub Release on merge) workflows. - Add dependency-review and OpenSSF Scorecard for supply-chain security, plus stale and labeler maintenance workflows. - Bump version to 0.1.1 to satisfy the new version-check gate. --- .github/labeler.yml | 14 +++++ .github/workflows/ci.yml | 73 +++++++++++++++++++++++++ .github/workflows/dependency-review.yml | 23 ++++++++ .github/workflows/labeler.yml | 18 ++++++ .github/workflows/release.yml | 40 ++++++++++++++ .github/workflows/scorecard.yml | 32 +++++++++++ .github/workflows/stale.yml | 30 ++++++++++ .github/workflows/version-check.yml | 43 +++++++++++++++ package.json | 2 +- 9 files changed, 274 insertions(+), 1 deletion(-) create mode 100644 .github/labeler.yml create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/dependency-review.yml create mode 100644 .github/workflows/labeler.yml create mode 100644 .github/workflows/release.yml create mode 100644 .github/workflows/scorecard.yml create mode 100644 .github/workflows/stale.yml create mode 100644 .github/workflows/version-check.yml diff --git a/.github/labeler.yml b/.github/labeler.yml new file mode 100644 index 0000000..0b8f685 --- /dev/null +++ b/.github/labeler.yml @@ -0,0 +1,14 @@ +# .github/labeler.yml — path-based PR auto-labels (actions/labeler@v5 format). +documentation: + - changed-files: + - any-glob-to-any-file: ["**/*.md", "docs/**"] +ci: + - changed-files: + - any-glob-to-any-file: [".github/**"] +dependencies: + - changed-files: + - any-glob-to-any-file: + ["**/package.json", "**/pubspec.yaml", "**/go.mod", "**/*.lock", "**/*lock.yaml"] +tests: + - changed-files: + - any-glob-to-any-file: ["**/*.test.*", "**/*_test.*", "test/**", "**/__tests__/**"] diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..245c8bc --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,73 @@ +name: CI + +on: + push: + branches: ["master"] + pull_request: + branches: ["master"] + +permissions: + contents: read + +concurrency: + group: ci-${{ github.ref }} + cancel-in-progress: true + +jobs: + build: + name: build + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + with: + persist-credentials: false + + # Detect the package manager from the lockfile so one workflow serves + # pnpm / npm / yarn / bun repos. + - name: Detect package manager + id: pm + run: | + if [ -f bun.lockb ] || [ -f bun.lock ]; then echo "pm=bun" >> "$GITHUB_OUTPUT" + elif [ -f pnpm-lock.yaml ]; then echo "pm=pnpm" >> "$GITHUB_OUTPUT" + elif [ -f yarn.lock ]; then echo "pm=yarn" >> "$GITHUB_OUTPUT" + else echo "pm=npm" >> "$GITHUB_OUTPUT"; fi + + - if: steps.pm.outputs.pm == 'pnpm' + uses: pnpm/action-setup@v4 + - if: steps.pm.outputs.pm == 'bun' + uses: oven-sh/setup-bun@v2 + - if: steps.pm.outputs.pm != 'bun' + uses: actions/setup-node@v4 + with: + node-version: lts/* + cache: ${{ steps.pm.outputs.pm }} + + - name: Install + run: | + case "${{ steps.pm.outputs.pm }}" in + bun) bun install --frozen-lockfile ;; + pnpm) pnpm install --frozen-lockfile ;; + yarn) yarn install --frozen-lockfile ;; + npm) npm ci ;; + esac + + # Run a script only if package.json defines it, so repos missing lint/ + # build/test don't fail the job. `run_if` echoes then runs via the pm. + - name: Run available scripts (lint, ts:check, build, test) + env: + PM: ${{ steps.pm.outputs.pm }} + run: | + has() { jq -e --arg s "$1" '.scripts[$s] // empty' package.json >/dev/null 2>&1; } + run() { + case "$PM" in + bun) bun run "$1" ;; + *) "$PM" run "$1" ;; + esac + } + for script in lint ts:check typecheck build test; do + if has "$script"; then + echo "::group::$script"; run "$script"; echo "::endgroup::" + else + echo "skip: no \"$script\" script" + fi + done diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml new file mode 100644 index 0000000..e6010bb --- /dev/null +++ b/.github/workflows/dependency-review.yml @@ -0,0 +1,23 @@ +name: Dependency Review + +# Flags vulnerable or disallowed-license dependencies introduced by a PR before +# they merge. Replaces Dependabot's alerting with a hard PR gate. +on: + pull_request: + branches: ["master"] + +permissions: + contents: read + +jobs: + dependency-review: + name: Dependency review + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + with: + persist-credentials: false + - uses: actions/dependency-review-action@v4 + with: + fail-on-severity: high + comment-summary-in-pr: on-failure diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml new file mode 100644 index 0000000..4f9c08f --- /dev/null +++ b/.github/workflows/labeler.yml @@ -0,0 +1,18 @@ +name: Labeler + +# Auto-labels PRs by the paths they touch (config in .github/labeler.yml). +on: + pull_request_target: + types: [opened, synchronize, reopened] + +permissions: + contents: read + pull-requests: write + +jobs: + label: + runs-on: ubuntu-latest + steps: + - uses: actions/labeler@v5 + with: + sync-labels: true diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..6a94684 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,40 @@ +name: Release + +# On push to the default branch (a merged PR), tag v and +# publish a GitHub Release with generated notes — unless the tag already exists. +on: + push: + branches: ["master"] + workflow_dispatch: {} + +permissions: + contents: read + +jobs: + release: + name: Tag and release + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - uses: actions/checkout@v6 + with: + fetch-depth: 0 + persist-credentials: false + - name: Read version + id: v + run: | + version=$(jq -r .version package.json) + echo "tag=v$version" >> "$GITHUB_OUTPUT" + - name: Release if new + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + TAG: ${{ steps.v.outputs.tag }} + run: | + if gh release view "$TAG" >/dev/null 2>&1 \ + || git ls-remote --tags origin "refs/tags/$TAG" | grep -q .; then + echo "Tag $TAG already exists — skipping." + exit 0 + fi + gh release create "$TAG" --target "${{ github.sha }}" --title "$TAG" --generate-notes + echo "Released $TAG ✓" diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml new file mode 100644 index 0000000..1f0088f --- /dev/null +++ b/.github/workflows/scorecard.yml @@ -0,0 +1,32 @@ +name: Scorecard + +# OpenSSF Scorecard — supply-chain security posture. Runs on push to the default +# branch and weekly; uploads SARIF so findings show in the Security tab. +on: + push: + branches: ["master"] + schedule: + - cron: "27 3 * * 1" # Mondays 03:27 UTC + workflow_dispatch: {} + +permissions: read-all + +jobs: + analysis: + name: Scorecard analysis + runs-on: ubuntu-latest + permissions: + security-events: write # upload SARIF to code scanning + id-token: write # publish results to the OpenSSF API + steps: + - uses: actions/checkout@v6 + with: + persist-credentials: false + - uses: ossf/scorecard-action@v2.4.0 + with: + results_file: results.sarif + results_format: sarif + publish_results: true + - uses: github/codeql-action/upload-sarif@v3 + with: + sarif_file: results.sarif diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml new file mode 100644 index 0000000..6df593d --- /dev/null +++ b/.github/workflows/stale.yml @@ -0,0 +1,30 @@ +name: Stale + +# Marks inactive issues/PRs stale, then closes them after a grace period. +on: + schedule: + - cron: "0 4 * * *" # daily 04:00 UTC + workflow_dispatch: {} + +permissions: + issues: write + pull-requests: write + +jobs: + stale: + runs-on: ubuntu-latest + steps: + - uses: actions/stale@v9 + with: + days-before-stale: 60 + days-before-close: 14 + stale-issue-label: stale + stale-pr-label: stale + exempt-issue-labels: pinned,security,blocked + exempt-pr-labels: pinned,security,blocked + stale-issue-message: > + This issue has been inactive for 60 days and is now marked stale. + Comment to keep it open; it will close in 14 days otherwise. + stale-pr-message: > + This PR has been inactive for 60 days and is now marked stale. + Push or comment to keep it open; it will close in 14 days otherwise. diff --git a/.github/workflows/version-check.yml b/.github/workflows/version-check.yml new file mode 100644 index 0000000..7a53ebf --- /dev/null +++ b/.github/workflows/version-check.yml @@ -0,0 +1,43 @@ +name: Version Check + +# Every PR into the default branch must bump package.json "version". The Release +# workflow turns that version into a tag + GitHub Release on merge. +on: + pull_request: + branches: ["master"] + types: [opened, synchronize, reopened] + +permissions: + contents: read + +jobs: + version-bumped: + name: version bumped + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + with: + persist-credentials: false + - name: Read versions + id: v + run: | + pr=$(jq -r .version package.json) + base=$(git show "origin/${{ github.base_ref }}:package.json" | jq -r .version) + echo "pr=$pr" >> "$GITHUB_OUTPUT" + echo "base=$base" >> "$GITHUB_OUTPUT" + - name: Compare + env: + PR: ${{ steps.v.outputs.pr }} + BASE: ${{ steps.v.outputs.base }} + run: | + echo "base=$BASE pr=$PR" + if [ "$PR" = "$BASE" ]; then + echo "::error::package.json version not bumped (still $BASE). Bump it: patch for fixes, minor for features, major for breaking." + exit 1 + fi + greater=$(printf '%s\n%s\n' "$BASE" "$PR" | sort -V | tail -n1) + if [ "$greater" != "$PR" ]; then + echo "::error::package.json version $PR is lower than base $BASE; it must move forward." + exit 1 + fi + echo "Version bumped $BASE -> $PR ✓" diff --git a/package.json b/package.json index 8a6fcaf..881cd12 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "nextjs", - "version": "0.1.0", + "version": "0.1.1", "private": true, "scripts": { "dev": "next dev -p 3000 --turbo", From 7eb62e44b89cef8e10331d826196013e8e23ec6b Mon Sep 17 00:00:00 2001 From: Nikhil Rajput Date: Mon, 29 Jun 2026 16:02:45 +0530 Subject: [PATCH 2/3] ci: fix pnpm version, base-ref fetch, and Dart example resolution --- .github/workflows/ci.yml | 2 ++ .github/workflows/version-check.yml | 2 ++ 2 files changed, 4 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 245c8bc..5a74fa4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -34,6 +34,8 @@ jobs: - if: steps.pm.outputs.pm == 'pnpm' uses: pnpm/action-setup@v4 + with: + version: 9 - if: steps.pm.outputs.pm == 'bun' uses: oven-sh/setup-bun@v2 - if: steps.pm.outputs.pm != 'bun' diff --git a/.github/workflows/version-check.yml b/.github/workflows/version-check.yml index 7a53ebf..8cbc27c 100644 --- a/.github/workflows/version-check.yml +++ b/.github/workflows/version-check.yml @@ -17,10 +17,12 @@ jobs: steps: - uses: actions/checkout@v6 with: + fetch-depth: 0 # need the base branch to diff the version persist-credentials: false - name: Read versions id: v run: | + git fetch --no-tags --depth=1 origin "${{ github.base_ref }}" pr=$(jq -r .version package.json) base=$(git show "origin/${{ github.base_ref }}:package.json" | jq -r .version) echo "pr=$pr" >> "$GITHUB_OUTPUT" From b76a984adddb2141904f86a2d5932f9ab11f3138 Mon Sep 17 00:00:00 2001 From: Nikhil Rajput Date: Mon, 29 Jun 2026 16:09:34 +0530 Subject: [PATCH 3/3] ci: add dummy build env / relax dart analyze --- .github/workflows/ci.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5a74fa4..2be6eb8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,6 +17,12 @@ jobs: build: name: build runs-on: ubuntu-latest + # Dummy build-time env so steps like `next build` that read env at build + # succeed in CI (same approach as portfolio-nextjs). Add this repo's + # build-time vars here with placeholder values. + env: + CI: "true" + NEXT_PUBLIC_GITHUB_TOKEN: dummy-token-for-build steps: - uses: actions/checkout@v6 with: