Contributing to Teams for Linux
Thank you for considering contributing! This guide will help you get started with development.
This project is a great starting point for learning Electron development!
Quick Start
- Fork the repository
- Clone your fork and create a feature branch
- Make changes (see architecture below)
- Test your changes with
npm start - Submit a pull request to
mainbranch
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:
- Go to the PR's "Checks" tab
- Select a workflow run
- Scroll to "Artifacts" section and download
Artifacts require GitHub login and are retained for 30 days.
Development Setup
Prerequisites
- Node.js and npm - Installation guide
- Git for version control
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
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- Useconstby default,letfor reassignment - async/await - Prefer over promise chains
- Private fields - Use
#propertysyntax 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-logfor 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
- Create
.mdor.mdxfiles indocs-site/docs/ - Update
docs-site/sidebars.tsto include new pages - Test locally with
npm run start - 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
Links and References
- 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
#123syntax 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:
```mermaidgraph TDA[Start] --> B[Process]B --> C[End]```
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
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 });
}
}
});
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 lintpasses without errors -
npm run test:e2epasses (E2E tests) - Manual testing completed
- Documentation updated if needed
- Commit messages are descriptive
PR Requirements
- Target branch: Always target
mainbranch - Description: Clearly describe changes and motivation
- Testing: Include testing instructions
- Screenshots: For UI changes, include before/after screenshots
- 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 settingsfix(notifications): resolve notification sound not playingdocs(api): update IPC channel documentation
Release Process
Releases are managed by release-please, which automatically maintains a Release PR from conventional commits:
- Merge PRs with conventional commit messages —
feat:,fix:,chore:, etc. - release-please creates/updates a Release PR — Includes version bump,
CHANGELOG.md, andappdata.xml - Merge the Release PR when ready — This triggers the build
- Build triggers automatically — On version change in main
- Promote GitHub draft → full release - Triggers Snap candidate channel and Flatpak
- Promote Snap candidate → stable - Manual step after testing
- 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
- IPC API Documentation - Inter-process communication reference
- Configuration Guide - Understanding the config system
- Architecture Overview - Main and renderer process layout, key components
Community Support
- Matrix Space: #teams-for-linux-space:matrix.org
- GitHub Discussions: Project discussions
- GitHub Issues: Bug reports and feature requests
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.
Related Documentation
- Configuration Options - Application configuration reference
- IPC API - Developer integration documentation
- Manual Release Process - Release workflow using release-please
- Release Info Generation - Technical details of release info script