Skip to main content

Contributing to Teams for Linux

Thank you for considering contributing! This guide will help you get started with development.

New to Electron?

This project is a great starting point for learning Electron development!

Quick Start

  1. Fork the repository
  2. Clone your fork and create a feature branch
  3. Make changes (see architecture below)
  4. Test your changes with npm start
  5. Submit a pull request to main branch

Each app/ subfolder contains a README explaining its purpose.

Testing Pull Requests

You can test PR changes without building from source by downloading pre-built artifacts from GitHub Actions.

How to Download PR Artifacts

A bot automatically posts a comment on each PR with direct download links to all build artifacts.

Alternatively:

  1. Go to the PR's "Checks" tab
  2. Select a workflow run
  3. Scroll to "Artifacts" section and download
info

Artifacts require GitHub login and are retained for 30 days.

Development Setup

Prerequisites

Getting Started

# Clone your fork
git clone https://github.com/your-username/teams-for-linux.git
cd teams-for-linux

# Install dependencies
npm install

# Run from source
npm start

# Lint code (required before commits)
npm run lint
Code Quality

Always run npm run lint before committing. Pull requests with linting errors will not be accepted.

Building

Local Linux Build

# Create all Linux packages (deb, rpm, snap, AppImage, tar.gz)
npm run dist:linux

# Development build without packaging
npm run pack

Docker/Podman Build

For consistent builds across environments:

podman run -it --rm --volume .:/var/mnt:z -w /var/mnt/ node:20 /bin/bash -c \
"apt update && apt install -y rpm && npm ci && npm run dist:linux"

Snap-specific Build

npm run dist:linux:snap
cd dist && sudo snap install teams-for-linux_*.snap --dangerous

Architecture Overview

Key Components

  • Main Process (app/index.js) - Application entry point (being refactored)
  • Startup (app/startup/) - Command line switches and initialization
  • Configuration (app/appConfiguration/) - Settings management
  • IPC System (app/ + browser scripts) - Process communication
  • Browser Integration (app/browser/) - Teams web app enhancements
  • System Features (notifications, tray, screen sharing)

Code Standards

Style Guidelines (some WIP)

  • ES6+ JavaScript - Use modern JavaScript features
  • No var - Use const by default, let for reassignment
  • async/await - Prefer over promise chains
  • Private fields - Use #property syntax for class private members
  • Arrow functions - For concise callbacks

Example Code Style

class ExampleModule {
#privateField = 'value';

constructor(config) {
this.config = config;
}

async performAction() {
try {
const result = await this.#processData();
return result;
} catch (error) {
console.error('Error in performAction:', error);
throw error;
}
}

#processData() {
// Private method implementation
return Promise.resolve(this.#privateField);
}
}

Error Handling

  • Use try-catch blocks for async operations
  • Log errors with context
  • Provide graceful degradation
  • Use electron-log for structured logging

Adding a Configuration Option

Configuration options are declared in one place, app/config/options.js, where each option is an object carrying a default, a describe string, a type, and an applyMode. This module is the single source of truth: the generated config reference, the docs-site config explorer, and startup validation are all derived from it. Adding one is a small, self-contained change that follows the shape of the options already there.

1. Declare the option

Add an entry to app/config/options.js, following the existing convention:

myFeatureEnabled: {
default: false,
describe: "Enable the my-feature behaviour.",
type: "boolean",
applyMode: "restart",
},

Use the type that matches the value (boolean, string, number, or object for a nested group of settings). The describe text is user-facing, so write it as a one-line explanation of what the option does.

Set applyMode to "restart" unless the option has a verified runtime re-read path; only then mark it "live". Defaulting to "restart" is always safe because the app's relaunch flow applies it.

For an object-typed option, also add a fields map describing each nested leaf as a dot-path with its own type and describe (leaf defaults are derived from the option's default by the generator):

myFeature: {
default: { enabled: false, intervalSeconds: 30 },
describe: "My-feature configuration.",
type: "object",
applyMode: "restart",
fields: {
"enabled": { type: "boolean", describe: "Enable the my-feature behaviour." },
"intervalSeconds": { type: "number", describe: "Poll interval in seconds." },
},
},

2. (Optional) Share the default with other modules

If the default needs to be read outside the config system — for example by a module or a unit test that should not initialise the full config — add it to app/config/defaults.js and reference it from options.js, the way meetupJoinRegEx already does.

3. Read the option

The parsed configuration object returned by app/config/index.js is passed through AppConfiguration. Read your option from that object where you need it, and treat the value as immutable after startup.

4. Regenerate the docs and schema

npm run generate-config-docs

This regenerates docs-site/docs/configuration-generated.md and docs-site/static/config-schema.json from app/config/options.js; commit both with your change (CI fails if they drift). The generator fails if any option is missing describe, type, or applyMode, or if an object option lacks fields — schema completeness is a merge gate, not a convention.

The same schema also drives warn-only validation of the user's config.json at startup (app/config/validator.js logs [CONFIG] warnings for unknown keys, type mismatches, and invalid choices, but never blocks boot), so a complete declaration is all the wiring your option needs.

5. Lint and test

npm run lint
npm run test:e2e

Documentation

Contributing to Documentation

The documentation is built with Docusaurus and automatically deployed to GitHub Pages.

Local Documentation Development

# Navigate to docs site
cd docs-site

# Install dependencies
npm install

# Start development server
npm run start

# Build for production
npm run build

Adding New Documentation

  1. Create .md or .mdx files in docs-site/docs/
  2. Update docs-site/sidebars.ts to include new pages
  3. Test locally with npm run start
  4. Commit and push changes

Documentation Standards

  • Use Docusaurus admonitions for callouts (:::note, :::tip, :::warning)
  • Include code examples for technical documentation
  • Add cross-references to related documentation
  • Use Mermaid diagrams for architecture visualization

Markdown Standards

When creating or updating any markdown documentation in this project (including documentation files, README files, task lists, and PRDs), leverage existing markdown library features instead of building custom solutions:

Content Structure

  • Table of Contents: Use GitHub's <!-- toc --> element or Docusaurus auto-TOC for automatic table of contents generation instead of manual lists
  • Collapsible Sections: Use GitHub's <details> and <summary> elements for optional or lengthy information

Callouts and Alerts

  • Callouts: Use GitHub's alert syntax for important information, warnings, and critical notes:
    • > [!NOTE] - For neutral informational notes
    • > [!TIP] - For helpful tips and best practices
    • > [!IMPORTANT] - For critical information users must know
    • > [!WARNING] - For warnings about potential issues
    • > [!CAUTION] - For dangerous actions or critical warnings
  • Docusaurus: In Docusaurus files, use admonitions: :::note, :::tip, :::warning, :::danger, :::info

Code and Technical Content

  • Code Blocks: Use proper syntax highlighting with language identifiers (e.g., ```javascript, ```bash, ```json)
  • Inline Code: Use backticks for inline code, commands, file paths, and variable names
  • Commands: Show shell commands with appropriate prompts and syntax highlighting

Data Presentation

  • Tables: Use standard markdown tables with proper alignment for structured data
  • Checkboxes: Use standard GitHub checkbox syntax - [ ] and - [x] for task tracking and checklists
  • Internal Links: Use relative paths for internal documentation links (e.g., [Configuration](../configuration.md))
  • External Links: Use absolute URLs for external resources
  • Issue/PR References: Use GitHub's #123 syntax for issue and pull request references

Diagrams and Visuals

  • Mermaid Diagrams: Use GitHub's Mermaid support for flowcharts, sequence diagrams, and architecture diagrams when applicable
  • Supported Types: flowchart, sequence, class, state, ER, gantt, pie, git graph
  • Example:
    ```mermaid
    graph TD
    A[Start] --> B[Process]
    B --> C[End]
    ```
Single Source of Truth

These markdown standards apply to ALL markdown files in the project:

  • Documentation site (docs-site/docs/)
  • Root-level documentation (README, CONTRIBUTING, CLAUDE.md)
  • Module READMEs in app/ directories

When updating standards, update this section only. All other files should reference these standards.

Testing

End-to-End (E2E) Tests

Teams for Linux uses Playwright for automated end-to-end testing. These tests ensure the application launches correctly and validates core functionality.

Running E2E Tests

# Run all E2E tests
npm run test:e2e

# Run tests in headed mode (visible browser)
npx playwright test --headed

# Run tests in debug mode
npx playwright test --debug
Clean State Testing

Each E2E test runs with a clean slate using a temporary user data directory. This ensures tests don't interfere with each other and are reproducible. The test harness uses the E2E_USER_DATA_DIR environment variable to provide isolated storage for each test run.

What E2E Tests Validate

Current E2E test coverage includes:

  • Application Launch: Verifies the app starts successfully
  • Window Creation: Ensures main window is created
  • Microsoft Login Redirect: Validates initial redirect to Microsoft authentication

Writing New E2E Tests

When contributing new features, consider adding E2E tests. Tests are located in tests/e2e/ and follow Playwright conventions.

Example test structure:

import { test, expect } from '@playwright/test';
import { _electron as electron } from 'playwright';
import { mkdtempSync, rmSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';

test('your feature test', async () => {
let electronApp;
let userDataDir;

try {
// Create clean state
userDataDir = mkdtempSync(join(tmpdir(), 'teams-e2e-'));

electronApp = await electron.launch({
args: ['./app/index.js'],
env: {
...process.env,
E2E_USER_DATA_DIR: userDataDir
}
});

const mainWindow = await electronApp.firstWindow();

// Your test logic here

} finally {
// Cleanup
if (electronApp) {
electronApp.process().kill('SIGTERM');
}
if (userDataDir) {
rmSync(userDataDir, { recursive: true, force: true });
}
}
});
Testing Strategy

For detailed information about the testing strategy and architecture decisions, see ADR-009: Automated Testing Strategy.

Manual Testing

# Run application in development mode
npm start

# Test specific features
npm start -- --user-data-dir=/tmp/test-profile

Build Testing

# Test production build
npm run pack
./dist/linux-unpacked/teams-for-linux

# Test package installation
sudo dpkg -i dist/teams-for-linux_*.deb
teams-for-linux

Pull Request Guidelines

Before Submitting

  • Code follows style guidelines
  • npm run lint passes without errors
  • npm run test:e2e passes (E2E tests)
  • Manual testing completed
  • Documentation updated if needed
  • Commit messages are descriptive

PR Requirements

  1. Target branch: Always target main branch
  2. Description: Clearly describe changes and motivation
  3. Testing: Include testing instructions
  4. Screenshots: For UI changes, include before/after screenshots
  5. Breaking changes: Clearly mark and document

Commit Message Format

type(scope): description

Longer explanation if needed

Fixes #123

Examples:

  • feat(config): add support for custom proxy settings
  • fix(notifications): resolve notification sound not playing
  • docs(api): update IPC channel documentation

Release Process

Releases are managed by release-please, which automatically maintains a Release PR from conventional commits:

  1. Merge PRs with conventional commit messagesfeat:, fix:, chore:, etc.
  2. release-please creates/updates a Release PR — Includes version bump, CHANGELOG.md, and appdata.xml
  3. Merge the Release PR when ready — This triggers the build
  4. Build triggers automatically — On version change in main
  5. Promote GitHub draft → full release - Triggers Snap candidate channel and Flatpak
  6. Promote Snap candidate → stable - Manual step after testing
Snap Channel Strategy
  • Pushes to main publish snaps to edge with a commit SHA suffix (e.g., 2.7.5-edge.g1a2b3c4)
  • Publishing a GitHub Release automatically builds and publishes to the candidate channel
  • Promotion from candidate to stable is manual

See Manual Release Process for detailed instructions.

Getting Help

Development Questions

Community Support

Code of Conduct

We are committed to providing a welcoming and inspiring community for all. Please be respectful and constructive in all interactions.

License

By contributing to Teams for Linux, you agree that your contributions will be licensed under the GPL-3.0 license.