Deployment Module
Overview
The Deployment module in @ever-works/agent manages the deployment lifecycle of work websites. It provides a unified facade over multiple deployment providers (Vercel, Netlify, etc.) and a comprehensive Git facade for repository management. Together, these facades handle everything from creating deployment projects and triggering builds to managing custom domains and monitoring deployment status.
Both facades follow the plugin-based provider resolution pattern, where the actual deployment and git operations are delegated to provider-specific plugins resolved dynamically from the plugin registry.
Module Structure
packages/agent/src/
facades/
deploy.facade.ts # Deployment provider facade
git.facade.ts # Git provider facade (~813 lines)
base.facade.ts # Abstract base class for all facades
facades.module.ts # NestJS module registering all facades
plugins/
services/
plugin-registry.service.ts # Plugin discovery and resolution
plugin-settings.service.ts # 4-level settings hierarchy
Key Classes and Services
DeployFacadeService
Implements IDeployFacade and provides deployment operations through plugin-based providers:
Core operations:
isConfigured()-- check if any deployment provider is availablegetAvailableProviders()-- list all registered deployment providers with their enabled statusvalidateToken(providerId, token)-- verify deployment credentialsgetTeams(providerId, token)-- list teams/organizations on the deployment platformdeploy(work, user, options)-- trigger a deployment. Connects the website repository to the deployment platform and initiates a build.getDeploymentStatus(work)-- check the current deployment state (building, ready, error)lookupExistingDeployment(work)-- find an existing deployment project for a workgetDeployToken(userId, providerId)-- retrieve stored deployment credentials
Domain management:
getDomains(work)-- list all domains (custom + default) for a deploymentaddDomain(work, domain)-- add a custom domain to the deploymentremoveDomain(work, domain)-- remove a custom domainverifyDomain(work, domain)-- check DNS configuration and SSL status
The database is the primary source of truth for domain records (WorkCustomDomain entity). Provider APIs are used for synchronization and verification.
Custom errors:
NoDeployProviderError-- no deployment provider configuredDeployProviderNotFoundError-- specified provider not found in registryNoDeployCredentialsError-- no valid credentials for the deployment provider
GitFacadeService
Implements IGitFacade and provides comprehensive Git operations (~813 lines):
Repository management:
getUser(options)-- get authenticated user infogetOrganizations(options)-- list user's organizationsgetRepository(owner, repo, options)-- get repository detailslistRepositories(owner, options)-- list repositoriescreateRepository(name, options)-- create a new repositorydeleteRepository(owner, repo, options)-- delete a repositoryupdateRepository(owner, repo, updates, options)-- update repository settingsforkRepository(owner, repo, options)-- fork a repositorycreateRepositoryFromTemplate(templateOwner, templateRepo, name, options)-- create from templaterepositoryExists(owner, repo, options)-- check if a repo existshasRepositoryAccess(owner, repo, options)-- check write access
Branch operations:
listBranches(owner, repo, options)createBranch(owner, repo, branchName, fromRef, options)deleteBranch(owner, repo, branchName, options)switchBranch(dir, branchName, options)renameBranch(owner, repo, oldName, newName, options)
Pull request operations:
createPullRequest(owner, repo, prData, options)getPullRequest(owner, repo, number, options)mergePullRequest(owner, repo, number, options)listPullRequests(owner, repo, filters, options)getPullRequestFiles(owner, repo, number, options)createPullRequestComment(owner, repo, number, body, options)closePullRequest(owner, repo, number, options)
Local Git operations (via isomorphic-git):
cloneOrPull(repoInfo, options)-- clone or update a local copypull(dir, options)-- pull latest changesadd(providerId, dir, filepath)-- stage filesaddAll(providerId, dir)-- stage all changescommit(providerId, dir, message)-- create a commitpush(pushOptions, gitOptions)-- push to remotegetCurrentBranch(dir)-- get current branch namegetMainBranch(owner, repo, options)-- detect default branchgetStatus(dir)-- get working tree status
File operations:
getFileContent(owner, repo, path, options)-- read a file from a remote repositorygetWorkContents(owner, repo, path, options)-- list work contents remotelygetReadme(owner, repo, options)-- fetch README contentgetRawFileUrl(providerId, owner, repo, branch, path)-- construct raw file URLgetWebUrl(providerId, owner, repo)-- construct repository web URL
Token resolution:
Git credentials are resolved in priority order:
- Explicit
tokenparameter inGitFacadeOptions - OAuth token from
OAuthTokenRepository(for the user and provider) - Personal access token (PAT) from plugin settings
Custom errors:
NoGitProviderError-- no git provider configuredGitProviderNotFoundError-- specified provider not foundNoGitCredentialsError-- no valid git credentials available
BaseFacadeService
Abstract base class providing shared functionality for all facades:
- Provider resolution:
resolvePlugin(providerOverride, userId, workId)resolves a plugin in priority order: explicit override > work active plugin > defaultForCapabilities > first enabled plugin - Settings hierarchy:
getResolvedSettings(pluginId, options)merges settings from Work > User > Admin > Plugin defaults - Typed settings access:
getSettingTyped(),getSettingRequired(),getSettingWithDefault()for safe settings retrieval
API Reference
DeployFacadeService
isConfigured(): boolean
getAvailableProviders(): Array<{ id: string; name: string; enabled: boolean }>
validateToken(providerId: string, token: string): Promise<boolean>
getTeams(providerId: string, token: string): Promise<DeployTeam[]>
deploy(work: Work, user: User, options?: DeployOptions): Promise<DeployResult>
getDeploymentStatus(work: Work): Promise<DeploymentStatus>
lookupExistingDeployment(work: Work): Promise<DeployProject | null>
getDeployToken(userId: string, providerId: string): Promise<string | null>
getDomains(work: Work): Promise<Domain[]>
addDomain(work: Work, domain: string): Promise<DomainResult>
removeDomain(work: Work, domain: string): Promise<void>
verifyDomain(work: Work, domain: string): Promise<DomainVerification>
GitFacadeService
// Options type used across all operations
interface GitFacadeOptions {
userId?: string;
providerId?: string;
token?: string;
}
isConfigured(): boolean
getUser(options: GitFacadeOptions): Promise<GitUser>
createRepository(name: string, options: GitFacadeOptions & CreateRepoOptions): Promise<GitRepository>
cloneOrPull(repoInfo: RepoInfo, options: GitFacadeOptions): Promise<string> // Returns local path
createPullRequest(owner: string, repo: string, pr: PullRequestData, options: GitFacadeOptions): Promise<GitPullRequest>
push(pushOptions: PushOptions, gitOptions: GitFacadeOptions): Promise<void>
// ... and ~30 more methods (see full source)
Configuration
Deployment Provider Settings
Deployment providers are configured through the plugin settings system:
// Plugin settings (via JSON Schema in plugin package.json)
{
"token": "ver_...", // Deployment platform API token (x-secret)
"teamId": "team_...", // Default team/org for deployments
"framework": "nextjs" // Framework preset
}
Git Provider Settings
{
"personalAccessToken": "ghp_...", // PAT for API operations (x-secret)
"defaultBranch": "main" // Default branch name
}
OAuth tokens are stored separately in the OAuthToken entity and take priority over PATs.
Dependencies
| Dependency | Purpose |
|---|---|
@ever-works/plugin | IDeployPlugin, IGitPlugin interfaces, PLUGIN_CAPABILITIES |
@ever-works/agent/plugins | PluginRegistryService, PluginSettingsService |
@ever-works/agent/database | WorkCustomDomain, OAuthTokenRepository |
isomorphic-git | Local git clone, commit, push operations |
Usage Examples
Deploying a Work Website
import { DeployFacadeService } from '@ever-works/agent/facades';
const result = await deployFacade.deploy(work, user, {
teamId: 'team_abc123'
});
// Check deployment status
const status = await deployFacade.getDeploymentStatus(work);
console.log(status.state); // 'building' | 'ready' | 'error'
Managing Custom Domains
await deployFacade.addDomain(work, 'tools.example.com');
const verification = await deployFacade.verifyDomain(work, 'tools.example.com');
if (!verification.verified) {
console.log('DNS records needed:', verification.requiredRecords);
}
Git Repository Operations
import { GitFacadeService } from '@ever-works/agent/facades';
// Create a repository
const repo = await gitFacade.createRepository('my-work-data', {
userId: user.id,
providerId: 'github',
isPrivate: true,
description: 'Data repository for My Work'
});
// Clone locally, make changes, commit, push
const localPath = await gitFacade.cloneOrPull(
{ owner: 'my-org', repo: 'my-work-data', committer: user.asCommitter() },
{ userId: user.id, providerId: 'github' }
);
await gitFacade.addAll('github', localPath);
await gitFacade.commit('github', localPath, 'Update item data');
await gitFacade.push({ dir: localPath }, { userId: user.id, providerId: 'github' });