Skip to main content

Record Web

Primary Workflow Quality Gate Status Coverage

For project tracking visit the Record Jira Board

What is Record Web

Record Web is an OTT video streaming web application built on the Accedo Assemble blueprint. It delivers a full end-to-end video service including content browsing, playback, authentication, profiles, and subscription management.

Getting Started

Project Setup

Prerequisites

Record Web uses pnpm as dependency manager, if you want to use another alternative, please go to package.json file and update the packageManager value.
For more information, please visit official node section about packageManager.
We use nvm to select proper node version.

Install dependencies and run app

Before installing dependencies, you will need to have a valid npm config for the @accedo scoped packages. Follow the instruction in https://accedobroadband.jira.com/wiki/spaces/BW/pages/168100346/How+to+Access+the+Accedo+Build+Web+npm+Modules#Setup-Accedo-Artifactory-with-scoped-packages to configure your npm using the required scoped packages.

Note: A SESSION_KEY environment variable is required in order to handle the session, so before anything, run:

openssl rand -hex 64

and paste it as SESSION_KEY=<your-generated-key> into your .env.local file

# First, install nvm, the node version used in this project is defined in the .nvmrc file.
nvm use
corepack enable pnpm
pnpm i
# Run the development server
pnpm dev
dev server watch

Sometimes .next folder with cached build chunks, and missing dependencies can cause issues when changing branch. .next folder is automatically removed when dev server is started, but pnpm dev needs to be restarted manually. use pnpm dev:watch to start dev server and automatically install dependencies and restart dev server when branch changes.

Debug the application

More details

Building locally

  • New relic secret values are injected when building on github. These are required for the build to complete. When building locally the same values need to be available, or set environment variable ENABLE_NEW_RELIC=false.
  • If build fails on github but works locally, one issue can be filename casing - build environment on github is case sensitive, but local environment may not be. git will also not always recognize file name changes when only the casing is changed, so file name changes may not have been committed as expected.

Repository Variables and Secrets

Create your repository variables and secrets in GitHub. Review Our environments variable type definition for runtime variables.

Variables:

  • CI_APPNAME_RECORD: Next.js application appname used in deployment and host
  • DOC_APPNAME_RECORD: Documentation appname used in deployment and host
  • STORYBOOK_APPNAME_RECORD: Storybook appname used in deployment and host
  • AWS_REGION: AWS region for deployments. Sync with DevOps.
  • NEW_RELIC_APP_NAME_DEV: New Relic application name (dev). Ask your Architect/PM.
  • NEW_RELIC_APP_NAME_PROD: New Relic application name (prod). Ask your Architect/PM.
Environment-based variables:
  • AWSARN_RECORD: AWS Amazon Resource Name Account Id. Sync with DevOps.
  • APP_DOMAIN: Application base domain. Sync with DevOps.
  • AWS_CLUSTERNAME_RECORD: Cluster name for the deployment. Sync with DevOps.
  • UNIFIED_API_BASEURL: Base URL of the Unified CMS+Config → NEXT_PUBLIC_UNIFIED_API_BASE_URL
  • OVP_BASEURL: Base URL of the OVP API → NEXT_PUBLIC_OVP_BASE_URL
  • IDP_BASEURL: Base URL of the IDP API → NEXT_PUBLIC_IDP_BASE_URL
  • PROFILE_BASEURL: Base URL of the Profile API → NEXT_PUBLIC_PROFILE_BASE_URL
  • SUBSCRIPTION_BASEURL: Base URL of the Subscription API → NEXT_PUBLIC_SUBSCRIPTION_BASE_URL
  • PLAYBACK_BASEURL: Base URL of the Playback API → NEXT_PUBLIC_PLAYBACK_BASE_URL
  • EPG_BASEURL: Base URL of the EPG API → NEXT_PUBLIC_EPG_BASE_URL

Secrets:

  • FIGMA_TOKEN: Figma API Token to fetch Design Tokens. Use a corporate token, not personal.
  • NPM_RESOLVER: npm token for @accedo private packages. Use a corporate token, not personal.
  • SLACK_WEBHOOK: Slack webhook token. Sync with DevOps/IT.
  • SONAR_TOKEN: SonarCloud access token. Sync with DevOps/IT.
Environment-based Secrets:
  • NEW_RELIC_LICENSE_KEY: New Relic license key. Ask your Architect/PM. One per environment.
  • UNIFIED_API_KEY: API key for the Unified CMS+Config service.

NPM Scripts

More details

Project App Structure

Record Web is a Next.js based application, so some conventions are based on the conventions defined in the framework itself, we recommend you to read at least App Routing Conventions

Component Structure

Inside this repo, we are following a Component structure based on two concepts:

  • Complexity-based: reusable components that can be used across any page/template, with a separation in two:
    • simple: stateless, very reusable components
    • complex: Usually composed from other simple components and can have state
  • Feature-based: every complex component, that is not going to be reusable, but just part of an specific feature (EPG, Auth, Player, ...)

Besides that, we need to take into account that Pages/views and partially, any possible route-based template will be created under the app folder following the App Routing Conventions from Next.js

The reason to keep this structure and not a pure Atom-based model is to avoid having all the components under the components folder, making very hard to traverse and look for the proper component, but limit it with the feature-based list of components and then simplify the 3-4 other layers (pages/views should be always under the app folder due to Next.js) into 2 at least initially to have a clear separation for the atomic, non-composed ones (and also stateless) from the others.

General Structure

📂 docs                       ───────> project documentation
📂 public ───────> web static assets
📂 scripts ───────> general scripts used locally
📂 src ───────> source dir
├─ 📂 app ───────> next.js routes
│ └─ 📂 [[...routeSegments]] ───────> Single dynamic route page (more on the next section)
│ └─ 📄 layout ───────> default page layout
│ ...
├─ 📂 components ───────> Complexity-based Components folder
│ └─ 📂 simple ───────> Simple Components (Not composed and without state or non-migrated/In progress/on a Figma branch Reels components)
│ └─ 📂 base ───────> Reels Components (Component from the Component library based on PD&A Reels) non-migrated/In progress/on a Figma branch
│ └─ 📂 complex ───────> Complex Components (composed from simple components or with state)
├─ 📂 config ───────> Application configuration
├─ 📂 context ───────> App global React contexts
├─ 📂 dataModels ───────> Any app data model
├─ 📂 dataTypes ───────> Data types generated from api documentation
├─ 📂 dev-utils ───────> Utilities used for development purposes
├─ 📂 features ───────> Feature-based Components folder
│ └─ 📂 ... ───────> A per feature folder, it will include all the needed code outside of the Data Fetching related one
├─ 📂 hooks ───────> App hooks, use for common shared utilities fns and Service access
├─ 📂 stores ───────> Global state management using Zustand, for shared application state
├─ 📂 providers ───────> App Providers (as per the Service Architecture)
├─ 📂 services ───────> App Services (as per the Service Architecture)
├─ 📂 types ───────> Typescript type utilities and global types
├─ 📂 utils ───────> Global utils
├─ 📂 views ───────> Application dynamic views (mapped from [[...routeSegments]]) and mapper
📄 .env ───────> environment variables
📄 next-env.d.ts ───────> next.js type defs
📄 next.config.js ───────> next.js config
📄 package.json ───────> project readme
📄 [other configs] ───────> Any other config file

Routes Mapping

More details

Project Documentation

General Documentation

Documentation for Record Web is created using Docusaurus inside the docs folder. There's a separate "project" inside that folder with its own npm project structure and so on.

The initial page uses this README.md file to create and display its content

To access documentation:

cd docs
pnpm start

To create documentation build:

cd docs
pnpm run build
pnpm run serve

If you need to add any extra Documentation, please add a new entry into docs/docs

You can use the helper command from the root of the repository to access the documentation:

pnpm run docs

Diagrams

Record Web uses mermaid for the diagrams which integrates directly with Github and with an integration with Docusaurus

CI/CD Workflows

Record Web uses GitHub Actions for continuous integration and deployment. The workflow architecture is designed for maintainability, reusability, and proper environment management with approval gates.

Workflow Architecture

The CI/CD system is built around a unified orchestrator pattern with specialized reusable workflows:

primary.yml (orchestrator)
├── setup → determines environment, PR context, deployment decisions
├── validations → lint, type-check, tests
├── update-pr → PR decoration with deployment URLs
├── cleanup → delete namespace (PR close only)
├── release → semantic versioning + version output (develop/staging/main only)
├── deploy-ci → Build & deploy main app (uses release version)
├── deploy-doc → Build & deploy documentation
└── deploy-storybook → Build & deploy Storybook

Primary Workflow (.github/workflows/primary.yml)

Triggers:

  • Pull request events: opened, synchronize, reopened, ready_for_review, edited, labeled, closed
  • Push to branches: main, staging, develop

Purpose: Single entry point that orchestrates all CI/CD operations based on event type and context.

How it works:

  1. Setup Job - Analyzes the event context and sets outputs for all downstream jobs:

    • Determines environment (prod, uat, or dev) based on branch
    • Extracts PR number if applicable
    • Sets boolean flags: is_pr, is_pr_closed, should_deploy, should_cleanup, should_release
  2. Conditional Execution - All downstream jobs use setup outputs to decide whether to run:

    if: needs.setup.outputs.should_deploy == 'true'
  3. Concurrency Control - Prevents duplicate runs:

    concurrency:
    group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event_name }}
    cancel-in-progress: false

Use Cases:

  • PR Workflow: Opens PR → triggers validations + deployments to dev-{appname}-{PR#} namespace
  • Branch Workflow: Push to develop → creates release + deploys to dev environment
  • Staging Release: Push to staging → creates release candidate + deploys to uat
  • Production Release: Push to main → creates release + deploys to prod (requires approval)
  • PR Cleanup: Close PR → deletes namespace and resources

Validation Workflows

Validate Workflow (.github/workflows/validate.yml)

Type: Reusable workflow (workflow_call)

Purpose: Runs all code quality checks before deployment.

Jobs:

  • ESLint validation
  • TypeScript type checking
  • Prettier formatting check
  • CSS linting
  • Spell checking
  • Unit tests with coverage reporting
  • SonarCloud analysis

Use Cases:

  • Runs on every PR to ensure code quality
  • Gates deployment - must pass before deploy jobs run
  • Provides early feedback on code issues

Deployment Workflows

Build & Deploy Workflows

  • Build-&-Deploy_webci-record.yml - Main application
  • Build-&-Deploy_doc-record-web.yml - Documentation site
  • Build-&-Deploy_storybook-record-web.yml - Storybook component library

Type: Reusable workflows

Purpose: Build Docker images and deploy to Kubernetes/ECS.

How they work:

  1. Checkout code
  2. Configure AWS credentials
  3. Build and push Docker image to ECR
  4. Deploy to Kubernetes using Helm
  5. Send Slack notification with deployment status

Namespace Convention:

  • PRs: {NAMESPACE}-{PR_NUMBER} (e.g., dev-record-web-12)
  • Branches: {NAMESPACE} (e.g., dev-record-web, uat-record-web, prod-record-web)

Use Cases:

  • PR deployments for testing
  • Staging deployments to UAT
  • Production deployments
  • Multiple isolated environments per PR

Cleanup Workflow (.github/workflows/cleanup.yml)

Type: Reusable workflow

Triggers: PR close events (called from primary workflow)

Purpose: Delete namespace and resources when PR is closed/merged.

How it works:

  1. Receives PR number and environment as inputs
  2. Configures AWS credentials
  3. Deletes Kubernetes namespace: {namespace}-{PR_NUMBER}
  4. Removes all associated resources
  5. Sends Slack notification

Use Cases:

  • Automatic cleanup on PR merge
  • Cost optimization - removes unused environments
  • Prevents namespace accumulation

Release Workflow (.github/workflows/release.yml)

Type: Reusable workflow

Triggers: Push to develop, staging, or main

Purpose: Create GitHub releases with semantic versioning and expose the version to downstream deploy jobs.

How it works:

  1. Analyzes commit messages for version bumps (conventional commits)
  2. Generates changelog from commits
  3. Creates GitHub release with tag
  4. Publishes release notes
  5. Outputs version — consumed by deploy jobs to set NEXT_PUBLIC_APP_VERSION at build time

Version format per environment:

  • develop / PR: 0.0.0-pr.{PR_NUMBER} (PRs) or pre-release tag (develop push)
  • staging: release candidate (e.g. 0.1.0-rc.1)
  • main / prod: clean semver (e.g. 0.1.0)

Use Cases:

  • Automated versioning across all environments
  • Version visible in the app's Settings page
  • Changelog generation
  • Release tracking

GitHub Environments & Approval Gates

Record Web leverages GitHub Environments to provide deployment control, security, and reusability.

Environment Configuration

Three environments configured:

  1. dev - Development environment

    • No restrictions
    • Automatic deployments
    • Used for PR previews and develop branch
  2. uat - User Acceptance Testing

    • Optional: Can require approval from QA team
    • Used for staging branch
    • Pre-production validation
  3. prod - Production environment

    • Requires approval from designated reviewers
    • Branch protection: Only main branch can deploy
    • Production-grade resources
    • Audit logging enabled

How Environments Enable Reusability

1. Environment-Specific Secrets & Variables

Each environment has its own set of secrets/variables accessed automatically:

jobs:
deploy:
environment: ${{ inputs.environment }} # 'dev', 'uat', or 'prod'
steps:
- name: Deploy
env:
AWS_ARN: ${{ secrets.AWSARN }} # Automatically uses dev/uat/prod ARN
APP_DOMAIN: ${{ vars.APP_DOMAIN }} # Automatically uses dev/uat/prod domain

Example Variables per Environment:

  • AWSARN_RECORD (per env)
  • APP_DOMAIN (per env)
  • AWS_CLUSTERNAME_RECORD (per env)
  • NEW_RELIC_LICENSE_KEY (per env)

2. Single Workflow for Multiple Environments

The same Build-&-Deploy_webci-record.yml workflow deploys to dev, uat, and prod:

# Primary workflow calls deployment
deploy-ci:
uses: ./.github/workflows/Build-&-Deploy_webci-record.yml
with:
environment: ${{ needs.setup.outputs.environment }} # Dynamic!
pr_number: ${{ needs.setup.outputs.pr_number }}
secrets: inherit

Benefits:

  • No workflow duplication
  • Consistent deployment process across environments
  • Single source of truth for deployment logic
  • Easier maintenance and updates

Approval Gates for Production

How it works:

  1. Deployment Request

    • When primary workflow targets prod environment
    • GitHub pauses the workflow execution
    • Sends notification to configured reviewers
  2. Review Process

    • Reviewers see deployment request in GitHub UI
    • Can view all deployment details (commit, changes, previous runs)
    • Must explicitly approve or reject
  3. Deployment Execution

    • On approval → workflow continues to production
    • On rejection → workflow stops, deployment prevented
    • All actions logged for audit trail

Configuration Example (GitHub UI):

Environment: prod
├── Required reviewers: @devops-team, @tech-lead
├── Wait timer: 0 minutes
├── Deployment branches: main only
└── Environment secrets:
├── AWSARN_RECORD
├── NEW_RELIC_LICENSE_KEY
└── ...

Real-World Flow:

Developer pushes to main branch

Primary workflow triggers

Setup job determines environment = 'prod'

Validation jobs run (lint, test, etc.)

Deploy job requests 'prod' environment

⏸️ WORKFLOW PAUSES - Approval Required

Reviewers receive notification

Reviewer approves deployment

✅ Deployment proceeds to production

Release job creates GitHub release

Slack notification sent

Security Benefits:

  • Prevents accidental production deployments
  • Enforces change control process
  • Audit trail for compliance
  • Branch protection (only mainprod)
  • Time-based deployment windows (optional)

Use Cases:

  • Production deployments require explicit approval
  • QA sign-off before UAT deployment (optional)
  • Emergency hotfix approvals
  • Compliance and audit requirements

Best Practices

  1. Branch Strategy:
    • Feature branches → PRs → Deploy to dev
    • develop → Auto-deploy to dev
    • staging → Auto-deploy to uat (with approval if configured)
    • main → Requires approval → Deploy to prod
  2. Cleanup: Always close/merge PRs to trigger automatic cleanup
  3. Monitoring: Check Slack notifications for deployment status
  4. Approvals: Configure production reviewers for approval gates

For more details about the Component documentation and usage read Component documentation

Data types

Data types for interacting with backend are imported directly from swagger documentation using pnpm run sync-data-types.

Code rules

Code and CSS Style Guidelines

We are using eslint, prettier and stylelint to define the project code style and ensure that everyone follows the same rules.

We enforce the usage of typescript as default language to ensure better type check and remove possible situations/issues.

typed-css-modules is used to enforce typing for css modules so only existing module classes can be used in typescript files. pnpm generate-css-types task can be run alone and will generate type files for all css modules, but types need to be generated for build and for type validation and continuosly during development, so this has been setup to run automatically.

Please refer to the .eslintrc.json and .stylelintrc.json to see the specific configuration we use for each tool. You can also read the Next.js Docs about the default eslint config.

  • Do not disable linting rules in code unless there is no other option. if a rule is disabled then a comment describing why must always be included.

App Styling

Record Web uses CSS Modules together with Design tokens to create the styles for the applications and components.

Style entry point file is globals.css and it includes all the Design System tokens from variables.css.

Custom pages/layout styles, component styles or features styles should be included in its own folder and use Design System tokens when possible.

note

Font Family is not used based on the Design tokens variables but using next/font/google instead as it's the standard way of working on Next.js

Font styles

Manually defining font styles in each instance should be avoided. Instead, implement the font styles defined in Figma in fontStyles.module.css so they can be reused wherever needed, like

.className {
composes: body-small-regular-sm from '@/app/fontStyles.module.css';
}

Refer to how this is implemented in the existing styles. If a font style is missing from fontStyles.module.css, add it there before using it.
See fontStyles.module.css for more details.

Spelling

cspell is used to ensure spelling in code and documentation. cspell will only capture spelling mistakes, not grammar mistakes. Spellcheck is run automatically when committing files.

cspall may report false positives, so whitelisted words are maintained in cspell.json -

  • words - any word to add as a valid word, may be suggested as correction
  • ignoreWords - words to exclude from spellcheck and ignore, will not be suggested as a correction

use /* cspell:disable-next-line -- [explanation] */ in file for false positives, for example picking up "Fmovies" as a spelling mistake in "redirectTo=%2Fmovies", or // cspell:ignore lcov at top of file if the false positives appear multiple times in the file.

Testing

We use jest, testing-library, storybook and playwright for the Automatic testing of the application.

  • jest is used as test runner and assertion library
  • testing-library is used for the component and component interaction library on the simple cases.
  • storybook is used for the component interaction test for more detailed cases

Branching and Code Reviews

We have a default branch develop and usually we work toward that branch every time, we don't use long-live branches or any other alternative. Our philosophy is to Ship as fast as possible into develop and use the setup validations to ensure everything works as expected.

We follow a Ship/Show/Ask approach where:

  • We Ship — apply the SHIP label for changes that are straightforward and don't require peer review.
  • We Show — apply the SHOW label for changes that don't require review but benefit from visibility or feedback.
  • We Ask — apply the ASK label for changes that require peer review before merging.

For all the cases, there's a PR template for Github located in .github/pull_request_template.md

Branch Naming conventions

We use short-lived feature branches to handle features work, and bugfix branches for any fix needed.

The general naming convention for branches:

  • {type}/RECORD-XXXX-short-description: for ticket related branches (e.g. feature/RECORD-1234-add-buffer-tracking)
  • {type}/short-description: for non-ticket branches (e.g. chore/update-dependencies)
  • release/vX.Y.Z: for release branches (e.g. release/v1.2.0)

Where type is one of: feature, fix, hotfix, chore, docs, ci

For personal or agent-assisted short-lived work: {username}/{type}/RECORD-XXXX-short-description

Github Actions
Validations

All CI/CD is orchestrated by a single primary.yml workflow that handles PRs and branch pushes. It splits into validations (validations.yml) and deployments.

The validations will run:

  • All the code validations (linters and tsc)
  • Unit and component test validation with code coverage report
  • Component Accessibility Validation using Storybook
  • Danger Pull request analysis
  • Sonar Analysis with report to SonarCloud

After all the static validations, primary.yml triggers the CI/CD workflows based on event context (e.g. edited events skip deployment as they include no code changes).

CI/CD

Three deployment workflows as workflow_call:

  • Nextjs Application deployment on Build-&-Deploy_webci-record.yml
  • Documentation deployment on Build-&-Deploy_doc-record-web.yml
  • Component Documentation deployment on Build-&-Deploy_storybook-record-web.yml

Deployment URLs follow the pattern {APPNAME}.{APP_DOMAIN} where appnames are configured as GitHub environment variables (CI_APPNAME_RECORD, DOC_APPNAME_RECORD, STORYBOOK_APPNAME_RECORD).

PR deployments use the namespace {APPNAME}-{PR_NUMBER}.{APP_DOMAIN}.

Note: PR related deployments will only be triggered if the following labels are included in the PR:

  • storybook for component Documentation deployment
  • docusaurus for Documentation deployment
Danger PR Analysis

We use Danger to do some automatic validations of the "quality" of the Pull request and also to report back into the Pull Request based on the base branch and the Status of the Pull Request.

You can find the specific code in the Dangerfile file.

PR title format (enforced by Danger): type(scope): description

  • type must be one of: feat, fix, hotfix, chore, docs, ci, refactor, style, test, perf, build, revert
  • scope is optional
  • ticket-reference is optional, included as free text in the description
  • Examples:
    • feat(player): RECORD-1234 add buffer tracking (with scope and ticket)
    • chore: update dependencies (no scope, no ticket)

Commit messages guide

We enforce the usage of conventional commits to create explicit commit messages.
We use commitlint with @commitlint/config-conventional and husky to ensure that the commits follow the proper messages. Please don't skip hooks.

Allowed commit types: feat, fix, hotfix, docs, style, refactor, test, chore, build, ci, perf, revert

Use conventional-commit-types for type usage definition

Versioning and Releases

Together with the Commit rules, we do automatic versioning based on the rules applied on conventional commits following Semantic Versioning

For the releases, we use Semantic Release with it's own Github Action to define a Workflow to ensure every merge into the main branches creates its own separate Record Web version.

You can see how is configured in:

It is in charge of the following:

  • Determine the version to create based on the commits since the previous tag based on the commitlint conventions
  • Execute the version change using @semantic-release/exec and set-version
  • Generate Changelog for both Github Release and CHANGELOG.md file
  • Create and push tag with the proper name/version
  • Create Github Release with the proper name/version

Releases are created automatically on every push to develop, staging, and main via semantic-release, driven by commit message types.

Creating Releases

Releases are fully automated through the release.yml workflow triggered on pushes to develop, staging, and main.

Version format

  • develop push: pre-release (e.g. 0.1.0-dev.1)
  • staging push: release candidate (e.g. 0.1.0-rc.1)
  • main push: stable release (e.g. 0.1.0)

The version is also injected into the app at build time via NEXT_PUBLIC_APP_VERSION and is visible in the Settings page.

  1. Merge all desired changes to develop via PRs
  2. Semantic Release automatically determines the version bump from commit types, updates CHANGELOG.md, creates a GitHub Release tag, and outputs the version to the deploy jobs
  3. The deploy jobs pick up the version and build the app with NEXT_PUBLIC_APP_VERSION set