Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 76 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
name: Release Workflow

on:
push:
tags:
- '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.
Comment thread
coderabbitai[bot] marked this conversation as resolved.
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@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
with:
persist-credentials: false

- name: Setup Node.js
uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # 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@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@c95fe1489396fe8a9eb87c0abf8aa5b2ef267fda # v2
with:
generate_release_notes: true
draft: false
prerelease: false
66 changes: 66 additions & 0 deletions docs/RELEASE_WORKFLOW.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# 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
```
Comment thread
coderabbitai[bot] marked this conversation as resolved.

4. The GitHub Action will automatically run tests and generate a changelog using `softprops/action-gh-release`.
Loading