From aad45d32059a921fa20f91073f9eab26d799c1de Mon Sep 17 00:00:00 2001 From: Bappadala Rohith Kumar Naidu <198095685+rohitkumarnaidu@users.noreply.github.com> Date: Thu, 6 Aug 2026 23:18:43 +0530 Subject: [PATCH 1/2] feat: Implement automated release workflow using GitHub Actions --- .github/workflows/release.yml | 59 ++++++++++++++++++++++++++++++++ docs/RELEASE_WORKFLOW.md | 64 +++++++++++++++++++++++++++++++++++ 2 files changed, 123 insertions(+) create mode 100644 .github/workflows/release.yml create mode 100644 docs/RELEASE_WORKFLOW.md diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 000000000..dd54e18ba --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,59 @@ +name: Release Workflow + +on: + push: + tags: + - 'v*' # Trigger only when a version tag is pushed (e.g., v1.0.0) + +# Principle of Least Privilege: Default token has NO permissions. +permissions: {} + +jobs: + build-and-test: + name: Build & Test + runs-on: ubuntu-latest + # Explicitly define read-only access for this job + permissions: + contents: read + steps: + - name: Checkout Code + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + cache: 'npm' + + - name: Install Dependencies + run: npm ci + + - name: Run Linters + run: npm run lint + + - name: Run Type Check + run: npm run type-check + + - name: Run Unit Tests + run: npm run test + + - name: Build Application + run: npm run build + + release: + name: Create GitHub Release + runs-on: ubuntu-latest + needs: build-and-test + # This is the ONLY job that requires write access + permissions: + contents: write + steps: + - name: Checkout Code + uses: actions/checkout@v4 + + - name: Generate Release Notes & Create Release + uses: softprops/action-gh-release@v2 + with: + generate_release_notes: true + draft: false + prerelease: false diff --git a/docs/RELEASE_WORKFLOW.md b/docs/RELEASE_WORKFLOW.md new file mode 100644 index 000000000..dd5727f22 --- /dev/null +++ b/docs/RELEASE_WORKFLOW.md @@ -0,0 +1,64 @@ +# Automated Release Workflow + +This document outlines the GitHub Actions workflow designed for securely building, testing, and automatically creating GitHub releases for `ossfolio`. + +## 🏗️ Architecture & Flow + +To adhere to the **Principle of Least Privilege**, our GitHub Actions workflow separates the read-only build and test processes from the write-enabled release process. This guarantees that potentially untrusted code execution during `npm install` or test runs cannot tamper with repository releases or tags. + +```mermaid +flowchart TD + %% Trigger + TagPush([Push git tag v*]) --> TriggerWorkflow[Trigger Release Workflow] + + %% Read-Only Job + subgraph BuildAndTest [Build & Test Job - Read-Only] + direction TB + Checkout1[Checkout Code] --> SetupNode[Setup Node.js v20] + SetupNode --> InstallDeps[npm ci] + InstallDeps --> Linter[Run Linter] + Linter --> TypeCheck[Run Type Check] + TypeCheck --> Tests[Run Unit Tests] + Tests --> Build[Run Next.js Build] + end + + TriggerWorkflow --> BuildAndTest + + %% Write Job + subgraph ReleaseJob [Release Job - Write Access] + direction TB + Checkout2[Checkout Code] --> ReleaseDraft[Generate Release Notes] + ReleaseDraft --> FinalRelease[Publish GitHub Release] + end + + %% Dependency + BuildAndTest -- "Success (Safe)" --> ReleaseJob + + %% Styling + classDef readOnly fill:#3b82f6,stroke:#1e3a8a,stroke-width:2px,color:#fff; + classDef writeAccess fill:#ef4444,stroke:#991b1b,stroke-width:2px,color:#fff; + + class Checkout1,SetupNode,InstallDeps,Linter,TypeCheck,Tests,Build readOnly; + class Checkout2,ReleaseDraft,FinalRelease writeAccess; +``` + +## 🔒 Security Best Practices + +1. **Explicit Permission Scopes**: The global workflow permission is intentionally set to `{}` (none). +2. **Job Isolation**: + - `build-and-test`: Explicitly granted `contents: read` to access the code. + - `release`: Explicitly granted `contents: write` to allow the action to publish the generated GitHub Release. +3. **Strict Dependencies**: The `release` job uses `needs: build-and-test`. If any linter, type check, or unit test fails, the workflow immediately aborts, ensuring broken code is never released. + +## 🚀 How to Create a Release + +To trigger this workflow and publish a new release: + +1. Update your `package.json` version. +2. Commit your changes to `main`. +3. Create and push a new tag following semantic versioning (e.g., `v1.0.0`): + ```bash + git tag v1.0.0 + git push origin v1.0.0 + ``` +4. The GitHub Action will automatically run tests and generate a changelog using `softprops/action-gh-release`. From 5282396f57c6f30630eb7eb1720b883f0eadf334 Mon Sep 17 00:00:00 2001 From: Bappadala Rohith Kumar Naidu <198095685+rohitkumarnaidu@users.noreply.github.com> Date: Thu, 6 Aug 2026 23:32:13 +0530 Subject: [PATCH 2/2] chore: apply CodeRabbit suggestions for secure release workflow --- .github/workflows/release.yml | 27 ++++++++++++++++++++++----- docs/RELEASE_WORKFLOW.md | 2 ++ 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index dd54e18ba..d7985f61e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -3,7 +3,11 @@ name: Release Workflow on: push: tags: - - 'v*' # Trigger only when a version tag is pushed (e.g., v1.0.0) + - 'v[0-9]+.[0-9]+.[0-9]+' # Trigger only on semantic version tags + +concurrency: + group: ${{ github.ref }} + cancel-in-progress: true # Principle of Least Privilege: Default token has NO permissions. permissions: {} @@ -17,10 +21,12 @@ jobs: contents: read steps: - name: Checkout Code - uses: actions/checkout@v4 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + with: + persist-credentials: false - name: Setup Node.js - uses: actions/setup-node@v4 + uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4 with: node-version: '20' cache: 'npm' @@ -49,10 +55,21 @@ jobs: contents: write steps: - name: Checkout Code - uses: actions/checkout@v4 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + with: + persist-credentials: false + + - name: Validate Semantic Version Tag + run: | + TAG_VERSION=${GITHUB_REF_NAME#v} + PACKAGE_VERSION=$(node -p "require('./package.json').version") + if [ "$TAG_VERSION" != "$PACKAGE_VERSION" ]; then + echo "Error: Tag version (v$TAG_VERSION) does not match package.json version ($PACKAGE_VERSION)." + exit 1 + fi - name: Generate Release Notes & Create Release - uses: softprops/action-gh-release@v2 + uses: softprops/action-gh-release@c95fe1489396fe8a9eb87c0abf8aa5b2ef267fda # v2 with: generate_release_notes: true draft: false diff --git a/docs/RELEASE_WORKFLOW.md b/docs/RELEASE_WORKFLOW.md index dd5727f22..c7140834e 100644 --- a/docs/RELEASE_WORKFLOW.md +++ b/docs/RELEASE_WORKFLOW.md @@ -57,8 +57,10 @@ To trigger this workflow and publish a new release: 1. Update your `package.json` version. 2. Commit your changes to `main`. 3. Create and push a new tag following semantic versioning (e.g., `v1.0.0`): + ```bash git tag v1.0.0 git push origin v1.0.0 ``` + 4. The GitHub Action will automatically run tests and generate a changelog using `softprops/action-gh-release`.