Compare commits

..

41 Commits

Author SHA1 Message Date
Kyle Carberry fe925a39e9 Fix Windows build 2022-03-18 07:36:17 +00:00
Kyle Carberry 519043890b Improve workspace create flow 2022-03-18 07:16:21 +00:00
Kyle Carberry 8146d002e9 Move templates to examples 2022-03-17 14:11:12 +00:00
Kyle Carberry 2cc902f525 Use embedded templates instead of serving over API 2022-03-16 20:54:10 +00:00
Kyle Carberry fc8c950529 Cleanup readwriter 2022-03-16 17:18:26 +00:00
Kyle Carberry def36fa93a Remove bubbletea 2022-03-16 17:14:42 +00:00
Kyle Carberry c8ae865eed Merge branch 'main' into workflow 2022-03-16 15:56:59 +00:00
Kyle Carberry 53d489ec19 Fix linting error 2022-03-16 15:51:41 +00:00
Kyle Carberry 8bc5c1afdf Fix compilation on Windows 2022-03-15 23:57:27 -05:00
Kyle Carberry 749d1ac64b Fix CloseReader 2022-03-16 00:45:44 +00:00
Kyle Carberry df885f57d5 Merge branch 'main' into workflow 2022-03-15 22:48:01 +00:00
Kyle Carberry 82bceec144 Fix PTY write newline 2022-03-15 22:38:00 +00:00
Kyle Carberry 52e4d994aa Fix write newline 2022-03-15 20:12:52 +00:00
Kyle Carberry 5677f22185 Disable promptui tests for Windows 2022-03-15 19:12:39 +00:00
Kyle Carberry 1005371a99 Fix embed 2022-03-15 18:53:08 +00:00
Kyle Carberry c7dfb519a6 Lower test requirements 2022-03-15 18:43:52 +00:00
Kyle Carberry 1402a08523 Merge branch 'workflow' of github.com:coder/coder into workflow 2022-03-15 17:44:29 +00:00
Kyle Carberry 369c4a0019 Merge branch 'workflow' of github.com:coder/coder into workflow 2022-03-15 17:44:17 +00:00
Kyle Carberry b4c2aed001 Merge branch 'workflow' of github.com:coder/coder into workflow 2022-03-15 17:40:32 +00:00
Kyle Carberry 6ac95bfca6 Fix templater 2022-03-15 17:40:24 +00:00
Kyle Carberry 6e53335ce7 Merge branch 'main' into workflow 2022-03-15 11:47:38 -05:00
Kyle Carberry 55a0160c2c Increase tunnel connect timeout 2022-03-15 16:43:36 +00:00
Kyle Carberry 8165782d9c Merge branch 'main' into workflow 2022-03-15 14:49:38 +00:00
Kyle Carberry e79f9b05da Skip login tests on Windows 2022-03-15 14:40:20 +00:00
Kyle Carberry 72bc74cb0a Update agent connection frequency 2022-03-15 14:23:56 +00:00
Kyle Carberry f895d1f11f Fix promptui test 2022-03-14 23:04:37 +00:00
Kyle Carberry 2b9a849f05 Fix leaking command 2022-03-14 22:47:58 +00:00
Kyle Carberry fdc17eee0d Merge branch 'main' into workflow 2022-03-14 22:23:48 +00:00
Kyle Carberry 1c9e124d6c Rename daemon to start 2022-03-14 22:22:39 +00:00
Kyle Carberry 926e59ea9d Remove unused CLI tests 2022-03-14 21:25:46 +00:00
Kyle Carberry cfffbe5c65 Fix linting errors 2022-03-14 21:21:16 +00:00
Kyle Carberry 8d24602cf5 Add clog 2022-03-14 19:48:22 +00:00
Kyle Carberry 21c3d1cf9e Fix agent lock 2022-03-14 18:14:49 +00:00
Kyle Carberry 6b4c8b5817 Add deletion of workspaces and projects 2022-03-13 18:45:41 +00:00
Kyle Carberry 99c8eb180d Tests pass! 2022-03-13 16:52:57 +00:00
Kyle Carberry c01b8f7d65 Add cancellation to provisionerd 2022-03-13 16:46:58 +00:00
Kyle Carberry b23a58c8fe It works! 2022-03-11 20:30:12 +00:00
Kyle Carberry 11e1d99625 It all works, but now with tea! 🧋 2022-03-11 03:02:24 +00:00
Kyle Carberry 8cee1fbdba Back to green tests! 2022-03-11 02:29:37 +00:00
Kyle Carberry 688ab17030 Move API structs to codersdk 2022-03-10 19:44:57 +00:00
Kyle Carberry 94e50698e7 Add templates 2022-03-10 18:54:40 +00:00
9803 changed files with 48742 additions and 1007827 deletions
-126
View File
@@ -1,126 +0,0 @@
# Coder Architecture
This document provides an overview of Coder's architecture and core systems.
## What is Coder?
Coder is a platform for creating, managing, and using remote development environments (also known as Cloud Development Environments or CDEs). It leverages Terraform to define and provision these environments, which are referred to as "workspaces" within the project. The system is designed to be extensible, secure, and provide developers with a seamless remote development experience.
## Core Architecture
The heart of Coder is a control plane that orchestrates the creation and management of workspaces. This control plane interacts with separate Provisioner processes over gRPC to handle workspace builds. The Provisioners consume workspace definitions and use Terraform to create the actual infrastructure.
The CLI package serves dual purposes - it can be used to launch the control plane itself and also provides client functionality for users to interact with an existing control plane instance. All user-facing frontend code is developed in TypeScript using React and lives in the `site/` directory.
The database layer uses PostgreSQL with SQLC for generating type-safe database code. Database migrations are carefully managed to ensure both forward and backward compatibility through paired `.up.sql` and `.down.sql` files.
## API Design
Coder's API architecture combines REST and gRPC approaches. The REST API is defined in `coderd/coderd.go` and uses Chi for HTTP routing. This provides the primary interface for the frontend and external integrations.
Internal communication with Provisioners occurs over gRPC, with service definitions maintained in `.proto` files. This separation allows for efficient binary communication with the components responsible for infrastructure management while providing a standard REST interface for human-facing applications.
## Network Architecture
Coder implements a secure networking layer based on Tailscale's Wireguard implementation. The `tailnet` package provides connectivity between workspace agents and clients through DERP (Designated Encrypted Relay for Packets) servers when direct connections aren't possible. This creates a secure overlay network allowing access to workspaces regardless of network topology, firewalls, or NAT configurations.
### Tailnet and DERP System
The networking system has three key components:
1. **Tailnet**: An overlay network implemented in the `tailnet` package that provides secure, end-to-end encrypted connections between clients, the Coder server, and workspace agents.
2. **DERP Servers**: These relay traffic when direct connections aren't possible. Coder provides several options:
- A built-in DERP server that runs on the Coder control plane
- Integration with Tailscale's global DERP infrastructure
- Support for custom DERP servers for lower latency or offline deployments
3. **Direct Connections**: When possible, the system establishes peer-to-peer connections between clients and workspaces using STUN for NAT traversal. This requires both endpoints to send UDP traffic on ephemeral ports.
### Workspace Proxies
Workspace proxies (in the Enterprise edition) provide regional relay points for browser-based connections, reducing latency for geo-distributed teams. Key characteristics:
- Deployed as independent servers that authenticate with the Coder control plane
- Relay connections for SSH, workspace apps, port forwarding, and web terminals
- Do not make direct database connections
- Managed through the `coder wsproxy` commands
- Implemented primarily in the `enterprise/wsproxy/` package
## Agent System
The workspace agent runs within each provisioned workspace and provides core functionality including:
- SSH access to workspaces via the `agentssh` package
- Port forwarding
- Terminal connectivity via the `pty` package for pseudo-terminal support
- Application serving
- Healthcheck monitoring
- Resource usage reporting
Agents communicate with the control plane using the tailnet system and authenticate using secure tokens.
## Workspace Applications
Workspace applications (or "apps") provide browser-based access to services running within workspaces. The system supports:
- HTTP(S) and WebSocket connections
- Path-based or subdomain-based access URLs
- Health checks to monitor application availability
- Different sharing levels (owner-only, authenticated users, or public)
- Custom icons and display settings
The implementation is primarily in the `coderd/workspaceapps/` directory with components for URL generation, proxying connections, and managing application state.
## Implementation Details
The project structure separates frontend and backend concerns. React components and pages are organized in the `site/src/` directory, with Jest used for testing. The backend is primarily written in Go, with a strong emphasis on error handling patterns and test coverage.
Database interactions are carefully managed through migrations in `coderd/database/migrations/` and queries in `coderd/database/queries/`. All new queries require proper database authorization (dbauthz) implementation to ensure that only users with appropriate permissions can access specific resources.
## Authorization System
The database authorization (dbauthz) system enforces fine-grained access control across all database operations. It uses role-based access control (RBAC) to validate user permissions before executing database operations. The `dbauthz` package wraps the database store and performs authorization checks before returning data. All database operations must pass through this layer to ensure security.
## Testing Framework
The codebase has a comprehensive testing approach with several key components:
1. **Parallel Testing**: All tests must use `t.Parallel()` to run concurrently, which improves test suite performance and helps identify race conditions.
2. **coderdtest Package**: This package in `coderd/coderdtest/` provides utilities for creating test instances of the Coder server, setting up test users and workspaces, and mocking external components.
3. **Integration Tests**: Tests often span multiple components to verify system behavior, such as template creation, workspace provisioning, and agent connectivity.
4. **Enterprise Testing**: Enterprise features have dedicated test utilities in the `coderdenttest` package.
## Open Source and Enterprise Components
The repository contains both open source and enterprise components:
- Enterprise code lives primarily in the `enterprise/` directory
- Enterprise features focus on governance, scalability (high availability), and advanced deployment options like workspace proxies
- The boundary between open source and enterprise is managed through a licensing system
- The same core codebase supports both editions, with enterprise features conditionally enabled
## Development Philosophy
Coder emphasizes clear error handling, with specific patterns required:
- Concise error messages that avoid phrases like "failed to"
- Wrapping errors with `%w` to maintain error chains
- Using sentinel errors with the "err" prefix (e.g., `errNotFound`)
All tests should run in parallel using `t.Parallel()` to ensure efficient testing and expose potential race conditions. The codebase is rigorously linted with golangci-lint to maintain consistent code quality.
Git contributions follow a standard format with commit messages structured as `type: <message>`, where type is one of `feat`, `fix`, or `chore`.
## Development Workflow
Development can be initiated using `scripts/develop.sh` to start the application after making changes. Database schema updates should be performed through the migration system using `create_migration.sh <name>` to generate migration files, with each `.up.sql` migration paired with a corresponding `.down.sql` that properly reverts all changes.
If the development database gets into a bad state, it can be completely reset by removing the PostgreSQL data directory with `rm -rf .coderv2/postgres`. This will destroy all data in the development database, requiring you to recreate any test users, templates, or workspaces after restarting the application.
Code generation for the database layer uses `coderd/database/generate.sh`, and developers should refer to `sqlc.yaml` for the appropriate style and patterns to follow when creating new queries or tables.
The focus should always be on maintaining security through proper database authorization, clean error handling, and comprehensive test coverage to ensure the platform remains robust and reliable.
-218
View File
@@ -1,218 +0,0 @@
# Database Development Patterns
## Database Work Overview
### Database Generation Process
1. Modify SQL files in `coderd/database/queries/`
2. Run `make gen`
3. If errors about audit table, update `enterprise/audit/table.go`
4. Run `make gen` again
5. Run `make lint` to catch any remaining issues
## Migration Guidelines
### Creating Migration Files
**Location**: `coderd/database/migrations/`
**Format**: `{number}_{description}.{up|down}.sql`
- Number must be unique and sequential
- Always include both up and down migrations
### Helper Scripts
| Script | Purpose |
|---------------------------------------------------------------------|-----------------------------------------|
| `./coderd/database/migrations/create_migration.sh "migration name"` | Creates new migration files |
| `./coderd/database/migrations/fix_migration_numbers.sh` | Renumbers migrations to avoid conflicts |
| `./coderd/database/migrations/create_fixture.sh "fixture name"` | Creates test fixtures for migrations |
### Database Query Organization
- **MUST DO**: Any changes to database - adding queries, modifying queries should be done in the `coderd/database/queries/*.sql` files
- **MUST DO**: Queries are grouped in files relating to context - e.g. `prebuilds.sql`, `users.sql`, `oauth2.sql`
- After making changes to any `coderd/database/queries/*.sql` files you must run `make gen` to generate respective ORM changes
## Handling Nullable Fields
Use `sql.NullString`, `sql.NullBool`, etc. for optional database fields:
```go
CodeChallenge: sql.NullString{
String: params.codeChallenge,
Valid: params.codeChallenge != "",
}
```
Set `.Valid = true` when providing values.
## Audit Table Updates
If adding fields to auditable types:
1. Update `enterprise/audit/table.go`
2. Add each new field with appropriate action:
- `ActionTrack`: Field should be tracked in audit logs
- `ActionIgnore`: Field should be ignored in audit logs
- `ActionSecret`: Field contains sensitive data
3. Run `make gen` to verify no audit errors
## Database Architecture
### Core Components
- **PostgreSQL 13+** recommended for production
- **Migrations** managed with `migrate`
- **Database authorization** through `dbauthz` package
### Authorization Patterns
```go
// Public endpoints needing system access (OAuth2 registration)
app, err := api.Database.GetOAuth2ProviderAppByClientID(dbauthz.AsSystemRestricted(ctx), clientID)
// Authenticated endpoints with user context
app, err := api.Database.GetOAuth2ProviderAppByClientID(ctx, clientID)
// System operations in middleware
roles, err := db.GetAuthorizationUserRoles(dbauthz.AsSystemRestricted(ctx), userID)
```
## Common Database Issues
### Migration Issues
1. **Migration conflicts**: Use `fix_migration_numbers.sh` to renumber
2. **Missing down migration**: Always create both up and down files
3. **Schema inconsistencies**: Verify against existing schema
### Field Handling Issues
1. **Nullable field errors**: Use `sql.Null*` types consistently
2. **Missing audit entries**: Update `enterprise/audit/table.go`
### Query Issues
1. **Query organization**: Group related queries in appropriate files
2. **Generated code errors**: Run `make gen` after query changes
3. **Performance issues**: Add appropriate indexes in migrations
## Database Testing
### Test Database Setup
```go
func TestDatabaseFunction(t *testing.T) {
db := dbtestutil.NewDB(t)
// Test with real database
result, err := db.GetSomething(ctx, param)
require.NoError(t, err)
require.Equal(t, expected, result)
}
```
## Best Practices
### Schema Design
1. **Use appropriate data types**: VARCHAR for strings, TIMESTAMP for times
2. **Add constraints**: NOT NULL, UNIQUE, FOREIGN KEY as appropriate
3. **Create indexes**: For frequently queried columns
4. **Consider performance**: Normalize appropriately but avoid over-normalization
### Query Writing
1. **Use parameterized queries**: Prevent SQL injection
2. **Handle errors appropriately**: Check for specific error types
3. **Use transactions**: For related operations that must succeed together
4. **Optimize queries**: Use EXPLAIN to understand query performance
### Migration Writing
1. **Make migrations reversible**: Always include down migration
2. **Test migrations**: On copy of production data if possible
3. **Keep migrations small**: One logical change per migration
4. **Document complex changes**: Add comments explaining rationale
## Advanced Patterns
### Complex Queries
```sql
-- Example: Complex join with aggregation
SELECT
u.id,
u.username,
COUNT(w.id) as workspace_count
FROM users u
LEFT JOIN workspaces w ON u.id = w.owner_id
WHERE u.created_at > $1
GROUP BY u.id, u.username
ORDER BY workspace_count DESC;
```
### Conditional Queries
```sql
-- Example: Dynamic filtering
SELECT * FROM oauth2_provider_apps
WHERE
($1::text IS NULL OR name ILIKE '%' || $1 || '%')
AND ($2::uuid IS NULL OR organization_id = $2)
ORDER BY created_at DESC;
```
### Audit Patterns
```go
// Example: Auditable database operation
func (q *sqlQuerier) UpdateUser(ctx context.Context, arg UpdateUserParams) (User, error) {
// Implementation here
// Audit the change
if auditor := audit.FromContext(ctx); auditor != nil {
auditor.Record(audit.UserUpdate{
UserID: arg.ID,
Old: oldUser,
New: newUser,
})
}
return newUser, nil
}
```
## Debugging Database Issues
### Common Debug Commands
```bash
# Check database connection
make test-postgres
# Run specific database tests
go test ./coderd/database/... -run TestSpecificFunction
# Check query generation
make gen
# Verify audit table
make lint
```
### Debug Techniques
1. **Enable query logging**: Set appropriate log levels
2. **Use database tools**: pgAdmin, psql for direct inspection
3. **Check constraints**: UNIQUE, FOREIGN KEY violations
4. **Analyze performance**: Use EXPLAIN ANALYZE for slow queries
### Troubleshooting Checklist
- [ ] Migration files exist (both up and down)
- [ ] `make gen` run after query changes
- [ ] Audit table updated for new fields
- [ ] Nullable fields use `sql.Null*` types
- [ ] Authorization context appropriate for endpoint type
-157
View File
@@ -1,157 +0,0 @@
# OAuth2 Development Guide
## RFC Compliance Development
### Implementing Standard Protocols
When implementing standard protocols (OAuth2, OpenID Connect, etc.):
1. **Fetch and Analyze Official RFCs**:
- Always read the actual RFC specifications before implementation
- Use WebFetch tool to get current RFC content for compliance verification
- Document RFC requirements in code comments
2. **Default Values Matter**:
- Pay close attention to RFC-specified default values
- Example: RFC 7591 specifies `client_secret_basic` as default, not `client_secret_post`
- Ensure consistency between database migrations and application code
3. **Security Requirements**:
- Follow RFC security considerations precisely
- Example: RFC 7592 prohibits returning registration access tokens in GET responses
- Implement proper error responses per protocol specifications
4. **Validation Compliance**:
- Implement comprehensive validation per RFC requirements
- Support protocol-specific features (e.g., custom schemes for native OAuth2 apps)
- Test edge cases defined in specifications
## OAuth2 Provider Implementation
### OAuth2 Spec Compliance
1. **Follow RFC 6749 for token responses**
- Use `expires_in` (seconds) not `expiry` (timestamp) in token responses
- Return proper OAuth2 error format: `{"error": "code", "error_description": "details"}`
2. **Error Response Format**
- Create OAuth2-compliant error responses for token endpoint
- Use standard error codes: `invalid_client`, `invalid_grant`, `invalid_request`
- Avoid generic error responses for OAuth2 endpoints
### PKCE Implementation
- Support both with and without PKCE for backward compatibility
- Use S256 method for code challenge
- Properly validate code_verifier against stored code_challenge
### UI Authorization Flow
- Use POST requests for consent, not GET with links
- Avoid dependency on referer headers for security decisions
- Support proper state parameter validation
### RFC 8707 Resource Indicators
- Store resource parameters in database for server-side validation (opaque tokens)
- Validate resource consistency between authorization and token requests
- Support audience validation in refresh token flows
- Resource parameter is optional but must be consistent when provided
## OAuth2 Error Handling Pattern
```go
// Define specific OAuth2 errors
var (
errInvalidPKCE = xerrors.New("invalid code_verifier")
)
// Use OAuth2-compliant error responses
type OAuth2Error struct {
Error string `json:"error"`
ErrorDescription string `json:"error_description,omitempty"`
}
// Return proper OAuth2 errors
if errors.Is(err, errInvalidPKCE) {
writeOAuth2Error(ctx, rw, http.StatusBadRequest, "invalid_grant", "The PKCE code verifier is invalid")
return
}
```
## Testing OAuth2 Features
### Test Scripts
Located in `./scripts/oauth2/`:
- `test-mcp-oauth2.sh` - Full automated test suite
- `setup-test-app.sh` - Create test OAuth2 app
- `cleanup-test-app.sh` - Remove test app
- `generate-pkce.sh` - Generate PKCE parameters
- `test-manual-flow.sh` - Manual browser testing
Always run the full test suite after OAuth2 changes:
```bash
./scripts/oauth2/test-mcp-oauth2.sh
```
### RFC Protocol Testing
1. **Compliance Test Coverage**:
- Test all RFC-defined error codes and responses
- Validate proper HTTP status codes for different scenarios
- Test protocol-specific edge cases (URI formats, token formats, etc.)
2. **Security Boundary Testing**:
- Test client isolation and privilege separation
- Verify information disclosure protections
- Test token security and proper invalidation
## Common OAuth2 Issues
1. **OAuth2 endpoints returning wrong error format** - Ensure OAuth2 endpoints return RFC 6749 compliant errors
2. **Resource indicator validation failing** - Ensure database stores and retrieves resource parameters correctly
3. **PKCE tests failing** - Verify both authorization code storage and token exchange handle PKCE fields
4. **RFC compliance failures** - Verify against actual RFC specifications, not assumptions
5. **Authorization context errors in public endpoints** - Use `dbauthz.AsSystemRestricted(ctx)` pattern
6. **Default value mismatches** - Ensure database migrations match application code defaults
7. **Bearer token authentication issues** - Check token extraction precedence and format validation
8. **URI validation failures** - Support both standard schemes and custom schemes per protocol requirements
## Authorization Context Patterns
```go
// Public endpoints needing system access (OAuth2 registration)
app, err := api.Database.GetOAuth2ProviderAppByClientID(dbauthz.AsSystemRestricted(ctx), clientID)
// Authenticated endpoints with user context
app, err := api.Database.GetOAuth2ProviderAppByClientID(ctx, clientID)
// System operations in middleware
roles, err := db.GetAuthorizationUserRoles(dbauthz.AsSystemRestricted(ctx), userID)
```
## OAuth2/Authentication Work Patterns
- Types go in `codersdk/oauth2.go` or similar
- Handlers go in `coderd/oauth2.go` or `coderd/identityprovider/`
- Database fields need migration + audit table updates
- Always support backward compatibility
## Protocol Implementation Checklist
Before completing OAuth2 or authentication feature work:
- [ ] Verify RFC compliance by reading actual specifications
- [ ] Implement proper error response formats per protocol
- [ ] Add comprehensive validation for all protocol fields
- [ ] Test security boundaries and token handling
- [ ] Update RBAC permissions for new resources
- [ ] Add audit logging support if applicable
- [ ] Create database migrations with proper defaults
- [ ] Add comprehensive test coverage including edge cases
- [ ] Verify linting compliance
- [ ] Test both positive and negative scenarios
- [ ] Document protocol-specific patterns and requirements
-212
View File
@@ -1,212 +0,0 @@
# Testing Patterns and Best Practices
## Testing Best Practices
### Avoiding Race Conditions
1. **Unique Test Identifiers**:
- Never use hardcoded names in concurrent tests
- Use `time.Now().UnixNano()` or similar for unique identifiers
- Example: `fmt.Sprintf("test-client-%s-%d", t.Name(), time.Now().UnixNano())`
2. **Database Constraint Awareness**:
- Understand unique constraints that can cause test conflicts
- Generate unique values for all constrained fields
- Test name isolation prevents cross-test interference
### Testing Patterns
- Use table-driven tests for comprehensive coverage
- Mock external dependencies
- Test both positive and negative cases
- Use `testutil.WaitLong` for timeouts in tests
### Test Package Naming
- **Test packages**: Use `package_test` naming (e.g., `identityprovider_test`) for black-box testing
## RFC Protocol Testing
### Compliance Test Coverage
1. **Test all RFC-defined error codes and responses**
2. **Validate proper HTTP status codes for different scenarios**
3. **Test protocol-specific edge cases** (URI formats, token formats, etc.)
### Security Boundary Testing
1. **Test client isolation and privilege separation**
2. **Verify information disclosure protections**
3. **Test token security and proper invalidation**
## Test Organization
### Test File Structure
```
coderd/
├── oauth2.go # Implementation
├── oauth2_test.go # Main tests
├── oauth2_test_helpers.go # Test utilities
└── oauth2_validation.go # Validation logic
```
### Test Categories
1. **Unit Tests**: Test individual functions in isolation
2. **Integration Tests**: Test API endpoints with database
3. **End-to-End Tests**: Full workflow testing
4. **Race Tests**: Concurrent access testing
## Test Commands
### Running Tests
| Command | Purpose |
|---------|---------|
| `make test` | Run all Go tests |
| `make test RUN=TestFunctionName` | Run specific test |
| `go test -v ./path/to/package -run TestFunctionName` | Run test with verbose output |
| `make test-postgres` | Run tests with Postgres database |
| `make test-race` | Run tests with Go race detector |
| `make test-e2e` | Run end-to-end tests |
### Frontend Testing
| Command | Purpose |
|---------|---------|
| `pnpm test` | Run frontend tests |
| `pnpm check` | Run code checks |
## Common Testing Issues
### Database-Related
1. **SQL type errors** - Use `sql.Null*` types for nullable fields
2. **Race conditions in tests** - Use unique identifiers instead of hardcoded names
### OAuth2 Testing
1. **PKCE tests failing** - Verify both authorization code storage and token exchange handle PKCE fields
2. **Resource indicator validation failing** - Ensure database stores and retrieves resource parameters correctly
### General Issues
1. **Missing newlines** - Ensure files end with newline character
2. **Package naming errors** - Use `package_test` naming for test files
3. **Log message formatting errors** - Use lowercase, descriptive messages without special characters
## Systematic Testing Approach
### Multi-Issue Problem Solving
When facing multiple failing tests or complex integration issues:
1. **Identify Root Causes**:
- Run failing tests individually to isolate issues
- Use LSP tools to trace through call chains
- Check both compilation and runtime errors
2. **Fix in Logical Order**:
- Address compilation issues first (imports, syntax)
- Fix authorization and RBAC issues next
- Resolve business logic and validation issues
- Handle edge cases and race conditions last
3. **Verification Strategy**:
- Test each fix individually before moving to next issue
- Use `make lint` and `make gen` after database changes
- Verify RFC compliance with actual specifications
- Run comprehensive test suites before considering complete
## Test Data Management
### Unique Test Data
```go
// Good: Unique identifiers prevent conflicts
clientName := fmt.Sprintf("test-client-%s-%d", t.Name(), time.Now().UnixNano())
// Bad: Hardcoded names cause race conditions
clientName := "test-client"
```
### Test Cleanup
```go
func TestSomething(t *testing.T) {
// Setup
client := coderdtest.New(t, nil)
// Test code here
// Cleanup happens automatically via t.Cleanup() in coderdtest
}
```
## Test Utilities
### Common Test Patterns
```go
// Table-driven tests
tests := []struct {
name string
input InputType
expected OutputType
wantErr bool
}{
{
name: "valid input",
input: validInput,
expected: expectedOutput,
wantErr: false,
},
// ... more test cases
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result, err := functionUnderTest(tt.input)
if tt.wantErr {
require.Error(t, err)
return
}
require.NoError(t, err)
require.Equal(t, tt.expected, result)
})
}
```
### Test Assertions
```go
// Use testify/require for assertions
require.NoError(t, err)
require.Equal(t, expected, actual)
require.NotNil(t, result)
require.True(t, condition)
```
## Performance Testing
### Load Testing
- Use `scaletest/` directory for load testing scenarios
- Run `./scaletest/scaletest.sh` for performance testing
### Benchmarking
```go
func BenchmarkFunction(b *testing.B) {
for i := 0; i < b.N; i++ {
// Function call to benchmark
_ = functionUnderTest(input)
}
}
```
Run benchmarks with:
```bash
go test -bench=. -benchmem ./package/path
```
-239
View File
@@ -1,239 +0,0 @@
# Troubleshooting Guide
## Common Issues
### Database Issues
1. **"Audit table entry missing action"**
- **Solution**: Update `enterprise/audit/table.go`
- Add each new field with appropriate action (ActionTrack, ActionIgnore, ActionSecret)
- Run `make gen` to verify no audit errors
2. **SQL type errors**
- **Solution**: Use `sql.Null*` types for nullable fields
- Set `.Valid = true` when providing values
- Example:
```go
CodeChallenge: sql.NullString{
String: params.codeChallenge,
Valid: params.codeChallenge != "",
}
```
### Testing Issues
3. **"package should be X_test"**
- **Solution**: Use `package_test` naming for test files
- Example: `identityprovider_test` for black-box testing
4. **Race conditions in tests**
- **Solution**: Use unique identifiers instead of hardcoded names
- Example: `fmt.Sprintf("test-client-%s-%d", t.Name(), time.Now().UnixNano())`
- Never use hardcoded names in concurrent tests
5. **Missing newlines**
- **Solution**: Ensure files end with newline character
- Most editors can be configured to add this automatically
### OAuth2 Issues
6. **OAuth2 endpoints returning wrong error format**
- **Solution**: Ensure OAuth2 endpoints return RFC 6749 compliant errors
- Use standard error codes: `invalid_client`, `invalid_grant`, `invalid_request`
- Format: `{"error": "code", "error_description": "details"}`
7. **Resource indicator validation failing**
- **Solution**: Ensure database stores and retrieves resource parameters correctly
- Check both authorization code storage and token exchange handling
8. **PKCE tests failing**
- **Solution**: Verify both authorization code storage and token exchange handle PKCE fields
- Check `CodeChallenge` and `CodeChallengeMethod` field handling
### RFC Compliance Issues
9. **RFC compliance failures**
- **Solution**: Verify against actual RFC specifications, not assumptions
- Use WebFetch tool to get current RFC content for compliance verification
- Read the actual RFC specifications before implementation
10. **Default value mismatches**
- **Solution**: Ensure database migrations match application code defaults
- Example: RFC 7591 specifies `client_secret_basic` as default, not `client_secret_post`
### Authorization Issues
11. **Authorization context errors in public endpoints**
- **Solution**: Use `dbauthz.AsSystemRestricted(ctx)` pattern
- Example:
```go
// Public endpoints needing system access
app, err := api.Database.GetOAuth2ProviderAppByClientID(dbauthz.AsSystemRestricted(ctx), clientID)
```
### Authentication Issues
12. **Bearer token authentication issues**
- **Solution**: Check token extraction precedence and format validation
- Ensure proper RFC 6750 Bearer Token Support implementation
13. **URI validation failures**
- **Solution**: Support both standard schemes and custom schemes per protocol requirements
- Native OAuth2 apps may use custom schemes
### General Development Issues
14. **Log message formatting errors**
- **Solution**: Use lowercase, descriptive messages without special characters
- Follow Go logging conventions
## Systematic Debugging Approach
YOU MUST ALWAYS find the root cause of any issue you are debugging
YOU MUST NEVER fix a symptom or add a workaround instead of finding a root cause, even if it is faster.
### Multi-Issue Problem Solving
When facing multiple failing tests or complex integration issues:
1. **Identify Root Causes**:
- Run failing tests individually to isolate issues
- Use LSP tools to trace through call chains
- Read Error Messages Carefully: Check both compilation and runtime errors
- Reproduce Consistently: Ensure you can reliably reproduce the issue before investigating
- Check Recent Changes: What changed that could have caused this? Git diff, recent commits, etc.
- When You Don't Know: Say "I don't understand X" rather than pretending to know
2. **Fix in Logical Order**:
- Address compilation issues first (imports, syntax)
- Fix authorization and RBAC issues next
- Resolve business logic and validation issues
- Handle edge cases and race conditions last
- IF your first fix doesn't work, STOP and re-analyze rather than adding more fixes
3. **Verification Strategy**:
- Always Test each fix individually before moving to next issue
- Verify Before Continuing: Did your test work? If not, form new hypothesis - don't add more fixes
- Use `make lint` and `make gen` after database changes
- Verify RFC compliance with actual specifications
- Run comprehensive test suites before considering complete
## Debug Commands
### Useful Debug Commands
| Command | Purpose |
|----------------------------------------------|---------------------------------------|
| `make lint` | Run all linters |
| `make gen` | Generate mocks, database queries |
| `go test -v ./path/to/package -run TestName` | Run specific test with verbose output |
| `go test -race ./...` | Run tests with race detector |
### LSP Debugging
#### Go LSP (Backend)
| Command | Purpose |
|----------------------------------------------------|------------------------------|
| `mcp__go-language-server__definition symbolName` | Find function definition |
| `mcp__go-language-server__references symbolName` | Find all references |
| `mcp__go-language-server__diagnostics filePath` | Check for compilation errors |
| `mcp__go-language-server__hover filePath line col` | Get type information |
#### TypeScript LSP (Frontend)
| Command | Purpose |
|----------------------------------------------------------------------------|------------------------------------|
| `mcp__typescript-language-server__definition symbolName` | Find component/function definition |
| `mcp__typescript-language-server__references symbolName` | Find all component/type usages |
| `mcp__typescript-language-server__diagnostics filePath` | Check for TypeScript errors |
| `mcp__typescript-language-server__hover filePath line col` | Get type information |
| `mcp__typescript-language-server__rename_symbol filePath line col newName` | Rename across codebase |
## Common Error Messages
### Database Errors
**Error**: `pq: relation "oauth2_provider_app_codes" does not exist`
- **Cause**: Missing database migration
- **Solution**: Run database migrations, check migration files
**Error**: `audit table entry missing action for field X`
- **Cause**: New field added without audit table update
- **Solution**: Update `enterprise/audit/table.go`
### Go Compilation Errors
**Error**: `package should be identityprovider_test`
- **Cause**: Test package naming convention violation
- **Solution**: Use `package_test` naming for black-box tests
**Error**: `cannot use X (type Y) as type Z`
- **Cause**: Type mismatch, often with nullable fields
- **Solution**: Use appropriate `sql.Null*` types
### OAuth2 Errors
**Error**: `invalid_client` but client exists
- **Cause**: Authorization context issue
- **Solution**: Use `dbauthz.AsSystemRestricted(ctx)` for public endpoints
**Error**: PKCE validation failing
- **Cause**: Missing PKCE fields in database operations
- **Solution**: Ensure `CodeChallenge` and `CodeChallengeMethod` are handled
## Prevention Strategies
### Before Making Changes
1. **Read the relevant documentation**
2. **Check if similar patterns exist in codebase**
3. **Understand the authorization context requirements**
4. **Plan database changes carefully**
### During Development
1. **Run tests frequently**: `make test`
2. **Use LSP tools for navigation**: Avoid manual searching
3. **Follow RFC specifications precisely**
4. **Update audit tables when adding database fields**
### Before Committing
1. **Run full test suite**: `make test`
2. **Check linting**: `make lint`
3. **Test with race detector**: `make test-race`
## Getting Help
### Internal Resources
- Check existing similar implementations in codebase
- Use LSP tools to understand code relationships
- For Go code: Use `mcp__go-language-server__*` commands
- For TypeScript/React code: Use `mcp__typescript-language-server__*` commands
- Read related test files for expected behavior
### External Resources
- Official RFC specifications for protocol compliance
- Go documentation for language features
- PostgreSQL documentation for database issues
### Debug Information Collection
When reporting issues, include:
1. **Exact error message**
2. **Steps to reproduce**
3. **Relevant code snippets**
4. **Test output (if applicable)**
5. **Environment information** (OS, Go version, etc.)
-227
View File
@@ -1,227 +0,0 @@
# Development Workflows and Guidelines
## Quick Start Checklist for New Features
### Before Starting
- [ ] Run `git pull` to ensure you're on latest code
- [ ] Check if feature touches database - you'll need migrations
- [ ] Check if feature touches audit logs - update `enterprise/audit/table.go`
## Development Server
### Starting Development Mode
- **Use `./scripts/develop.sh` to start Coder in development mode**
- This automatically builds and runs with `--dev` flag and proper access URL
- **⚠️ Do NOT manually run `make build && ./coder server --dev` - use the script instead**
### Development Workflow
1. **Always start with the development script**: `./scripts/develop.sh`
2. **Make changes** to your code
3. **The script will automatically rebuild** and restart as needed
4. **Access the development server** at the URL provided by the script
## Code Style Guidelines
### Go Style
- Follow [Effective Go](https://go.dev/doc/effective_go) and [Go's Code Review Comments](https://github.com/golang/go/wiki/CodeReviewComments)
- Create packages when used during implementation
- Validate abstractions against implementations
- **Test packages**: Use `package_test` naming (e.g., `identityprovider_test`) for black-box testing
### Error Handling
- Use descriptive error messages
- Wrap errors with context
- Propagate errors appropriately
- Use proper error types
- Pattern: `xerrors.Errorf("failed to X: %w", err)`
## Naming Conventions
- Names MUST tell what code does, not how it's implemented or its history
- Follow Go and TypeScript naming conventions
- When changing code, never document the old behavior or the behavior change
- NEVER use implementation details in names (e.g., "ZodValidator", "MCPWrapper", "JSONParser")
- NEVER use temporal/historical context in names (e.g., "LegacyHandler", "UnifiedTool", "ImprovedInterface", "EnhancedParser")
- NEVER use pattern names unless they add clarity (e.g., prefer "Tool" over "ToolFactory")
- Abbreviate only when obvious
### Comments
- Document exported functions, types, and non-obvious logic
- Follow JSDoc format for TypeScript
- Use godoc format for Go code
## Database Migration Workflows
### Migration Guidelines
1. **Create migration files**:
- Location: `coderd/database/migrations/`
- Format: `{number}_{description}.{up|down}.sql`
- Number must be unique and sequential
- Always include both up and down migrations
2. **Use helper scripts**:
- `./coderd/database/migrations/create_migration.sh "migration name"` - Creates new migration files
- `./coderd/database/migrations/fix_migration_numbers.sh` - Renumbers migrations to avoid conflicts
- `./coderd/database/migrations/create_fixture.sh "fixture name"` - Creates test fixtures for migrations
3. **Update database queries**:
- **MUST DO**: Any changes to database - adding queries, modifying queries should be done in the `coderd/database/queries/*.sql` files
- **MUST DO**: Queries are grouped in files relating to context - e.g. `prebuilds.sql`, `users.sql`, `oauth2.sql`
- After making changes to any `coderd/database/queries/*.sql` files you must run `make gen` to generate respective ORM changes
4. **Handle nullable fields**:
- Use `sql.NullString`, `sql.NullBool`, etc. for optional database fields
- Set `.Valid = true` when providing values
5. **Audit table updates**:
- If adding fields to auditable types, update `enterprise/audit/table.go`
- Add each new field with appropriate action (ActionTrack, ActionIgnore, ActionSecret)
- Run `make gen` to verify no audit errors
### Database Generation Process
1. Modify SQL files in `coderd/database/queries/`
2. Run `make gen`
3. If errors about audit table, update `enterprise/audit/table.go`
4. Run `make gen` again
5. Run `make lint` to catch any remaining issues
## API Development Workflow
### Adding New API Endpoints
1. **Define types** in `codersdk/` package
2. **Add handler** in appropriate `coderd/` file
3. **Register route** in `coderd/coderd.go`
4. **Add tests** in `coderd/*_test.go` files
5. **Update OpenAPI** by running `make gen`
## Testing Workflows
### Test Execution
- Run full test suite: `make test`
- Run specific test: `make test RUN=TestFunctionName`
- Run with Postgres: `make test-postgres`
- Run with race detector: `make test-race`
- Run end-to-end tests: `make test-e2e`
### Test Development
- Use table-driven tests for comprehensive coverage
- Mock external dependencies
- Test both positive and negative cases
- Use `testutil.WaitLong` for timeouts in tests
- Always use `t.Parallel()` in tests
## Commit Style
- Follow [Conventional Commits 1.0.0](https://www.conventionalcommits.org/en/v1.0.0/)
- Format: `type(scope): message`
- Types: `feat`, `fix`, `docs`, `style`, `refactor`, `test`, `chore`
- Keep message titles concise (~70 characters)
- Use imperative, present tense in commit titles
## Code Navigation and Investigation
### Using LSP Tools (STRONGLY RECOMMENDED)
**IMPORTANT**: Always use LSP tools for code navigation and understanding. These tools provide accurate, real-time analysis of the codebase and should be your first choice for code investigation.
#### Go LSP Tools (for backend code)
1. **Find function definitions** (USE THIS FREQUENTLY):
- `mcp__go-language-server__definition symbolName`
- Example: `mcp__go-language-server__definition getOAuth2ProviderAppAuthorize`
- Quickly jump to function implementations across packages
2. **Find symbol references** (ESSENTIAL FOR UNDERSTANDING IMPACT):
- `mcp__go-language-server__references symbolName`
- Locate all usages of functions, types, or variables
- Critical for refactoring and understanding data flow
3. **Get symbol information**:
- `mcp__go-language-server__hover filePath line column`
- Get type information and documentation at specific positions
#### TypeScript LSP Tools (for frontend code in site/)
1. **Find component/function definitions** (USE THIS FREQUENTLY):
- `mcp__typescript-language-server__definition symbolName`
- Example: `mcp__typescript-language-server__definition LoginPage`
- Quickly navigate to React components, hooks, and utility functions
2. **Find symbol references** (ESSENTIAL FOR UNDERSTANDING IMPACT):
- `mcp__typescript-language-server__references symbolName`
- Locate all usages of components, types, or functions
- Critical for refactoring React components and understanding prop usage
3. **Get type information**:
- `mcp__typescript-language-server__hover filePath line column`
- Get TypeScript type information and JSDoc documentation
4. **Rename symbols safely**:
- `mcp__typescript-language-server__rename_symbol filePath line column newName`
- Rename components, props, or functions across the entire codebase
5. **Check for TypeScript errors**:
- `mcp__typescript-language-server__diagnostics filePath`
- Get compilation errors and warnings for a specific file
### Investigation Strategy (LSP-First Approach)
#### Backend Investigation (Go)
1. **Start with route registration** in `coderd/coderd.go` to understand API endpoints
2. **Use Go LSP `definition` lookup** to trace from route handlers to actual implementations
3. **Use Go LSP `references`** to understand how functions are called throughout the codebase
4. **Follow the middleware chain** using LSP tools to understand request processing flow
5. **Check test files** for expected behavior and error patterns
#### Frontend Investigation (TypeScript/React)
1. **Start with route definitions** in `site/src/App.tsx` or router configuration
2. **Use TypeScript LSP `definition`** to navigate to React components and hooks
3. **Use TypeScript LSP `references`** to find all component usages and prop drilling
4. **Follow the component hierarchy** using LSP tools to understand data flow
5. **Check for TypeScript errors** with `diagnostics` before making changes
6. **Examine test files** (`.test.tsx`) for component behavior and expected props
## Troubleshooting Development Issues
### Common Issues
1. **Development server won't start** - Use `./scripts/develop.sh` instead of manual commands
2. **Database migration errors** - Check migration file format and use helper scripts
3. **Audit table errors** - Update `enterprise/audit/table.go` with new fields
4. **OAuth2 compliance issues** - Ensure RFC-compliant error responses
### Debug Commands
- Check linting: `make lint`
- Generate code: `make gen`
- Clean build: `make clean`
## Development Environment Setup
### Prerequisites
- Go (version specified in go.mod)
- Node.js and pnpm for frontend development
- PostgreSQL for database testing
- Docker for containerized testing
### First Time Setup
1. Clone the repository
2. Run `./scripts/develop.sh` to start development server
3. Access the development URL provided
4. Create admin user as prompted
5. Begin development
-133
View File
@@ -1,133 +0,0 @@
#!/bin/bash
# Claude Code hook script for file formatting
# This script integrates with the centralized Makefile formatting targets
# and supports the Claude Code hooks system for automatic file formatting.
set -euo pipefail
# A variable to memoize the command for canonicalizing paths.
_CANONICALIZE_CMD=""
# canonicalize_path resolves a path to its absolute, canonical form.
# It tries 'realpath' and 'readlink -f' in order.
# The chosen command is memoized to avoid repeated checks.
# If none of these are available, it returns an empty string.
canonicalize_path() {
local path_to_resolve="$1"
# If we haven't determined a command yet, find one.
if [[ -z "$_CANONICALIZE_CMD" ]]; then
if command -v realpath >/dev/null 2>&1; then
_CANONICALIZE_CMD="realpath"
elif command -v readlink >/dev/null 2>&1 && readlink -f . >/dev/null 2>&1; then
_CANONICALIZE_CMD="readlink"
else
# No command found, so we can't resolve.
# We set a "none" value to prevent re-checking.
_CANONICALIZE_CMD="none"
fi
fi
# Now, execute the command.
case "$_CANONICALIZE_CMD" in
realpath)
realpath "$path_to_resolve" 2>/dev/null
;;
readlink)
readlink -f "$path_to_resolve" 2>/dev/null
;;
*)
# This handles the "none" case or any unexpected error.
echo ""
;;
esac
}
# Read JSON input from stdin
input=$(cat)
# Extract the file path from the JSON input
# Expected format: {"tool_input": {"file_path": "/absolute/path/to/file"}} or {"tool_response": {"filePath": "/absolute/path/to/file"}}
file_path=$(echo "$input" | jq -r '.tool_input.file_path // .tool_response.filePath // empty')
# Secure path canonicalization to prevent path traversal attacks
# Resolve repo root to an absolute, canonical path.
repo_root_raw="$(cd "$(dirname "$0")/../.." && pwd)"
repo_root="$(canonicalize_path "$repo_root_raw")"
if [[ -z "$repo_root" ]]; then
# Fallback if canonicalization fails
repo_root="$repo_root_raw"
fi
# Resolve the input path to an absolute path
if [[ "$file_path" = /* ]]; then
# Already absolute
abs_file_path="$file_path"
else
# Make relative paths absolute from repo root
abs_file_path="$repo_root/$file_path"
fi
# Canonicalize the path (resolve symlinks and ".." segments)
canonical_file_path="$(canonicalize_path "$abs_file_path")"
# Check if canonicalization failed or if the resolved path is outside the repo
if [[ -z "$canonical_file_path" ]] || { [[ "$canonical_file_path" != "$repo_root" ]] && [[ "$canonical_file_path" != "$repo_root"/* ]]; }; then
echo "Error: File path is outside repository or invalid: $file_path" >&2
exit 1
fi
# Handle the case where the file path is the repository root itself.
if [[ "$canonical_file_path" == "$repo_root" ]]; then
echo "Warning: Formatting the repository root is not a supported operation. Skipping." >&2
exit 0
fi
# Convert back to relative path from repo root for consistency
file_path="${canonical_file_path#"$repo_root"/}"
if [[ -z "$file_path" ]]; then
echo "Error: No file path provided in input" >&2
exit 1
fi
# Check if file exists
if [[ ! -f "$file_path" ]]; then
echo "Error: File does not exist: $file_path" >&2
exit 1
fi
# Get the file extension to determine the appropriate formatter
file_ext="${file_path##*.}"
# Change to the project root directory (where the Makefile is located)
cd "$(dirname "$0")/../.."
# Call the appropriate Makefile target based on file extension
case "$file_ext" in
go)
make fmt/go FILE="$file_path"
echo "✓ Formatted Go file: $file_path"
;;
js | jsx | ts | tsx)
make fmt/ts FILE="$file_path"
echo "✓ Formatted TypeScript/JavaScript file: $file_path"
;;
tf | tfvars)
make fmt/terraform FILE="$file_path"
echo "✓ Formatted Terraform file: $file_path"
;;
sh)
make fmt/shfmt FILE="$file_path"
echo "✓ Formatted shell script: $file_path"
;;
md)
make fmt/markdown FILE="$file_path"
echo "✓ Formatted Markdown file: $file_path"
;;
*)
echo "No formatter available for file extension: $file_ext"
exit 0
;;
esac
-15
View File
@@ -1,15 +0,0 @@
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write|MultiEdit",
"hooks": [
{
"type": "command",
"command": ".claude/scripts/format.sh"
}
]
}
]
}
}
-1
View File
@@ -1 +0,0 @@
AGENTS.md
-82
View File
@@ -1,82 +0,0 @@
{
"name": "Development environments on your infrastructure",
"image": "codercom/oss-dogfood:latest",
"features": {
"ghcr.io/devcontainers/features/docker-in-docker:2": {
"moby": "false"
},
"ghcr.io/coder/devcontainer-features/code-server:1": {
"auth": "none",
"port": 13337
},
"./filebrowser": {
"folder": "${containerWorkspaceFolder}"
}
},
// SYS_PTRACE to enable go debugging
"runArgs": ["--cap-add=SYS_PTRACE"],
"customizations": {
"vscode": {
"extensions": ["biomejs.biome"]
},
"coder": {
"apps": [
{
"slug": "cursor",
"displayName": "Cursor Desktop",
"url": "cursor://coder.coder-remote/openDevContainer?owner=${localEnv:CODER_WORKSPACE_OWNER_NAME}&workspace=${localEnv:CODER_WORKSPACE_NAME}&agent=${localEnv:CODER_WORKSPACE_PARENT_AGENT_NAME}&url=${localEnv:CODER_URL}&token=$SESSION_TOKEN&devContainerName=${localEnv:CONTAINER_ID}&devContainerFolder=${containerWorkspaceFolder}&localWorkspaceFolder=${localWorkspaceFolder}",
"external": true,
"icon": "/icon/cursor.svg",
"order": 1
},
{
"slug": "windsurf",
"displayName": "Windsurf Editor",
"url": "windsurf://coder.coder-remote/openDevContainer?owner=${localEnv:CODER_WORKSPACE_OWNER_NAME}&workspace=${localEnv:CODER_WORKSPACE_NAME}&agent=${localEnv:CODER_WORKSPACE_PARENT_AGENT_NAME}&url=${localEnv:CODER_URL}&token=$SESSION_TOKEN&devContainerName=${localEnv:CONTAINER_ID}&devContainerFolder=${containerWorkspaceFolder}&localWorkspaceFolder=${localWorkspaceFolder}",
"external": true,
"icon": "/icon/windsurf.svg",
"order": 4
},
{
"slug": "zed",
"displayName": "Zed Editor",
"url": "zed://ssh/${localEnv:CODER_WORKSPACE_AGENT_NAME}.${localEnv:CODER_WORKSPACE_NAME}.${localEnv:CODER_WORKSPACE_OWNER_NAME}.coder${containerWorkspaceFolder}",
"external": true,
"icon": "/icon/zed.svg",
"order": 5
},
// Reproduce `code-server` app here from the code-server
// feature so that we can set the correct folder and order.
// Currently, the order cannot be specified via option because
// we parse it as a number whereas variable interpolation
// results in a string. Additionally we set health check which
// is not yet set in the feature.
{
"slug": "code-server",
"displayName": "code-server",
"url": "http://${localEnv:FEATURE_CODE_SERVER_OPTION_HOST:127.0.0.1}:${localEnv:FEATURE_CODE_SERVER_OPTION_PORT:8080}/?folder=${containerWorkspaceFolder}",
"openIn": "${localEnv:FEATURE_CODE_SERVER_OPTION_APPOPENIN:slim-window}",
"share": "${localEnv:FEATURE_CODE_SERVER_OPTION_APPSHARE:owner}",
"icon": "/icon/code.svg",
"group": "${localEnv:FEATURE_CODE_SERVER_OPTION_APPGROUP:Web Editors}",
"order": 3,
"healthCheck": {
"url": "http://${localEnv:FEATURE_CODE_SERVER_OPTION_HOST:127.0.0.1}:${localEnv:FEATURE_CODE_SERVER_OPTION_PORT:8080}/healthz",
"interval": 5,
"threshold": 2
}
}
]
}
},
"mounts": [
// Add a volume for the Coder home directory to persist shell history,
// and speed up dotfiles init and/or personalization.
"source=coder-coder-devcontainer-home,target=/home/coder,type=volume",
// Mount the entire home because conditional mounts are not supported.
// See: https://github.com/devcontainers/spec/issues/132
"source=${localEnv:HOME},target=/mnt/home/coder,type=bind,readonly"
],
"postCreateCommand": ["./.devcontainer/scripts/post_create.sh"],
"postStartCommand": ["./.devcontainer/scripts/post_start.sh"]
}
@@ -1,46 +0,0 @@
{
"id": "filebrowser",
"version": "0.0.1",
"name": "File Browser",
"description": "A web-based file browser for your development container",
"options": {
"port": {
"type": "string",
"default": "13339",
"description": "The port to run filebrowser on"
},
"folder": {
"type": "string",
"default": "",
"description": "The root directory for filebrowser to serve"
},
"baseUrl": {
"type": "string",
"default": "",
"description": "The base URL for filebrowser (e.g., /filebrowser)"
}
},
"entrypoint": "/usr/local/bin/filebrowser-entrypoint",
"dependsOn": {
"ghcr.io/devcontainers/features/common-utils:2": {}
},
"customizations": {
"coder": {
"apps": [
{
"slug": "filebrowser",
"displayName": "File Browser",
"url": "http://localhost:${localEnv:FEATURE_FILEBROWSER_OPTION_PORT:13339}",
"icon": "/icon/filebrowser.svg",
"order": 3,
"subdomain": true,
"healthcheck": {
"url": "http://localhost:${localEnv:FEATURE_FILEBROWSER_OPTION_PORT:13339}/health",
"interval": 5,
"threshold": 2
}
}
]
}
}
}
-54
View File
@@ -1,54 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
BOLD='\033[0;1m'
printf "%sInstalling filebrowser\n\n" "${BOLD}"
# Check if filebrowser is installed.
if ! command -v filebrowser &>/dev/null; then
VERSION="v2.42.1"
EXPECTED_HASH="7d83c0f077df10a8ec9bfd9bf6e745da5d172c3c768a322b0e50583a6bc1d3cc"
curl -fsSL "https://github.com/filebrowser/filebrowser/releases/download/${VERSION}/linux-amd64-filebrowser.tar.gz" -o /tmp/filebrowser.tar.gz
echo "${EXPECTED_HASH} /tmp/filebrowser.tar.gz" | sha256sum -c
tar -xzf /tmp/filebrowser.tar.gz -C /tmp
sudo mv /tmp/filebrowser /usr/local/bin/
sudo chmod +x /usr/local/bin/filebrowser
rm /tmp/filebrowser.tar.gz
fi
# Create entrypoint.
cat >/usr/local/bin/filebrowser-entrypoint <<EOF
#!/usr/bin/env bash
PORT="${PORT}"
FOLDER="${FOLDER:-}"
FOLDER="\${FOLDER:-\$(pwd)}"
BASEURL="${BASEURL:-}"
LOG_PATH=/tmp/filebrowser.log
export FB_DATABASE="\${HOME}/.filebrowser.db"
printf "🛠️ Configuring filebrowser\n\n"
# Check if filebrowser db exists.
if [[ ! -f "\${FB_DATABASE}" ]]; then
filebrowser config init >>\${LOG_PATH} 2>&1
filebrowser users add admin "" --perm.admin=true --viewMode=mosaic >>\${LOG_PATH} 2>&1
fi
filebrowser config set --baseurl=\${BASEURL} --port=\${PORT} --auth.method=noauth --root=\${FOLDER} >>\${LOG_PATH} 2>&1
printf "👷 Starting filebrowser...\n\n"
printf "📂 Serving \${FOLDER} at http://localhost:\${PORT}\n\n"
filebrowser >>\${LOG_PATH} 2>&1 &
printf "📝 Logs at \${LOG_PATH}\n\n"
EOF
chmod +x /usr/local/bin/filebrowser-entrypoint
printf "🥳 Installation complete!\n\n"
-67
View File
@@ -1,67 +0,0 @@
#!/bin/sh
install_devcontainer_cli() {
set -e
echo "🔧 Installing DevContainer CLI..."
cd "$(dirname "$0")/../tools/devcontainer-cli"
npm ci --omit=dev
ln -sf "$(pwd)/node_modules/.bin/devcontainer" "$(npm config get prefix)/bin/devcontainer"
}
install_ssh_config() {
echo "🔑 Installing SSH configuration..."
if [ -d /mnt/home/coder/.ssh ]; then
rsync -a /mnt/home/coder/.ssh/ ~/.ssh/
chmod 0700 ~/.ssh
else
echo "⚠️ SSH directory not found."
fi
}
install_git_config() {
echo "📂 Installing Git configuration..."
if [ -f /mnt/home/coder/git/config ]; then
rsync -a /mnt/home/coder/git/ ~/.config/git/
elif [ -d /mnt/home/coder/.gitconfig ]; then
rsync -a /mnt/home/coder/.gitconfig ~/.gitconfig
else
echo "⚠️ Git configuration directory not found."
fi
}
install_dotfiles() {
if [ ! -d /mnt/home/coder/.config/coderv2/dotfiles ]; then
echo "⚠️ Dotfiles directory not found."
return
fi
cd /mnt/home/coder/.config/coderv2/dotfiles || return
for script in install.sh install bootstrap.sh bootstrap script/bootstrap setup.sh setup script/setup; do
if [ -x $script ]; then
echo "📦 Installing dotfiles..."
./$script || {
echo "❌ Error running $script. Please check the script for issues."
return
}
echo "✅ Dotfiles installed successfully."
return
fi
done
echo "⚠️ No install script found in dotfiles directory."
}
personalize() {
# Allow script to continue as Coder dogfood utilizes a hack to
# synchronize startup script execution.
touch /tmp/.coder-startup-script.done
if [ -x /mnt/home/coder/personalize ]; then
echo "🎨 Personalizing environment..."
/mnt/home/coder/personalize
fi
}
install_devcontainer_cli
install_ssh_config
install_dotfiles
personalize
-4
View File
@@ -1,4 +0,0 @@
#!/bin/sh
# Start Docker service if not already running.
sudo service docker start
-26
View File
@@ -1,26 +0,0 @@
{
"name": "devcontainer-cli",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "devcontainer-cli",
"version": "1.0.0",
"dependencies": {
"@devcontainers/cli": "^0.80.0"
}
},
"node_modules/@devcontainers/cli": {
"version": "0.80.0",
"resolved": "https://registry.npmjs.org/@devcontainers/cli/-/cli-0.80.0.tgz",
"integrity": "sha512-w2EaxgjyeVGyzfA/KUEZBhyXqu/5PyWNXcnrXsZOBrt3aN2zyGiHrXoG54TF6K0b5DSCF01Rt5fnIyrCeFzFKw==",
"bin": {
"devcontainer": "devcontainer.js"
},
"engines": {
"node": "^16.13.0 || >=18.0.0"
}
}
}
}
@@ -1,8 +0,0 @@
{
"name": "devcontainer-cli",
"private": true,
"version": "1.0.0",
"dependencies": {
"@devcontainers/cli": "^0.80.0"
}
}
-28
View File
@@ -1,28 +0,0 @@
root = true
[*]
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
indent_style = tab
[*.{yaml,yml,tf,tftpl,tfvars,nix}]
indent_style = space
indent_size = 2
[*.proto]
indent_style = space
indent_size = 2
[coderd/database/dump.sql]
indent_style = space
indent_size = 4
[coderd/database/queries/*.sql]
indent_style = tab
indent_size = 4
[coderd/database/migrations/*.sql]
indent_style = tab
indent_size = 4
-7
View File
@@ -1,7 +0,0 @@
# If you would like `git blame` to ignore commits from this file, run...
# git config blame.ignoreRevsFile .git-blame-ignore-revs
# chore: format code with semicolons when using prettier (#9555)
988c9af0153561397686c119da9d1336d2433fdd
# chore: use tabs for prettier and biome (#14283)
95a7c0c4f087744a22c2e88dd3c5d30024d5fb02
+1 -18
View File
@@ -1,22 +1,5 @@
# Generated files
agent/agentcontainers/acmock/acmock.go linguist-generated=true
agent/agentcontainers/dcspec/dcspec_gen.go linguist-generated=true
agent/agentcontainers/testdata/devcontainercli/*/*.log linguist-generated=true
coderd/apidoc/docs.go linguist-generated=true
docs/reference/api/*.md linguist-generated=true
docs/reference/cli/*.md linguist-generated=true
coderd/apidoc/swagger.json linguist-generated=true
coderd/database/dump.sql linguist-generated=true
database/dump.sql linguist-generated=true
peerbroker/proto/*.go linguist-generated=true
provisionerd/proto/*.go linguist-generated=true
provisionerd/proto/version.go linguist-generated=false
provisionersdk/proto/*.go linguist-generated=true
*.tfplan.json linguist-generated=true
*.tfstate.json linguist-generated=true
*.tfstate.dot linguist-generated=true
*.tfplan.dot linguist-generated=true
site/e2e/google/protobuf/timestampGenerated.ts
site/e2e/provisionerGenerated.ts linguist-generated=true
site/src/api/countriesGenerated.tsx linguist-generated=true
site/src/api/rbacresourcesGenerated.tsx linguist-generated=true
site/src/api/typesGenerated.ts linguist-generated=true
-33
View File
@@ -1,33 +0,0 @@
dirs:
- docs
excludedDirs:
# Downstream bug in linkspector means large markdown files fail to parse
# but these are autogenerated and shouldn't need checking
- docs/reference
# Older changelogs may contain broken links
- docs/changelogs
ignorePatterns:
- pattern: "localhost"
- pattern: "example.com"
- pattern: "mailto:"
- pattern: "127.0.0.1"
- pattern: "0.0.0.0"
- pattern: "JFROG_URL"
- pattern: "coder.company.org"
# These real sites were blocking the linkspector action / GitHub runner IPs(?)
- pattern: "i.imgur.com"
- pattern: "code.visualstudio.com"
- pattern: "www.emacswiki.org"
- pattern: "linux.die.net/man"
- pattern: "www.gnu.org"
- pattern: "wiki.ubuntu.com"
- pattern: "mutagen.io"
- pattern: "docs.github.com"
- pattern: "claude.ai"
- pattern: "splunk.com"
- pattern: "stackoverflow.com/questions"
- pattern: "developer.hashicorp.com/terraform/language"
- pattern: "platform.openai.com"
- pattern: "api.openai.com"
aliveStatusCodes:
- 200
+1
View File
@@ -0,0 +1 @@
site @coder/coder-frontend
-79
View File
@@ -1,79 +0,0 @@
name: "🐞 Bug"
description: "File a bug report."
title: "bug: "
labels: ["needs-triage"]
type: "Bug"
body:
- type: checkboxes
id: existing_issues
attributes:
label: "Is there an existing issue for this?"
description: "Please search to see if an issue already exists for the bug you encountered."
options:
- label: "I have searched the existing issues"
required: true
- type: textarea
id: issue
attributes:
label: "Current Behavior"
description: "A concise description of what you're experiencing."
placeholder: "Tell us what you see!"
validations:
required: false
- type: textarea
id: logs
attributes:
label: "Relevant Log Output"
description: "Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks."
render: shell
- type: textarea
id: expected
attributes:
label: "Expected Behavior"
description: "A concise description of what you expected to happen."
validations:
required: false
- type: textarea
id: steps_to_reproduce
attributes:
label: "Steps to Reproduce"
description: "Provide step-by-step instructions to reproduce the issue."
placeholder: |
1. First step
2. Second step
3. Another step
4. Issue occurs
validations:
required: true
- type: textarea
id: environment
attributes:
label: "Environment"
description: |
Provide details about your environment:
- **Host OS**: (e.g., Ubuntu 24.04, Debian 12)
- **Coder Version**: (e.g., v2.18.4)
placeholder: |
Run `coder version` to get Coder version
value: |
- Host OS:
- Coder version:
validations:
required: false
- type: dropdown
id: additional_info
attributes:
label: "Additional Context"
description: "Select any applicable options:"
multiple: true
options:
- "The issue occurs consistently"
- "The issue is new (previously worked fine)"
- "The issue happens on multiple deployments"
- "I have tested this on the latest version"
-10
View File
@@ -1,10 +0,0 @@
contact_links:
- name: Questions, suggestion or feature requests?
url: https://github.com/coder/coder/discussions/new/choose
about: Our preferred starting point if you have any questions or suggestions about configuration, features or unexpected behavior.
- name: Coder Docs
url: https://coder.com/docs
about: Check our docs.
- name: Coder Discord Community
url: https://discord.gg/coder
about: Get in touch with the Coder developers and community for support.
@@ -1,49 +0,0 @@
name: "Download Embedded Postgres Cache"
description: |
Downloads the embedded postgres cache and outputs today's cache key.
A PR job can use a cache if it was created by its base branch, its current
branch, or the default branch.
https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/caching-dependencies-to-speed-up-workflows#restrictions-for-accessing-a-cache
outputs:
cache-key:
description: "Today's cache key"
value: ${{ steps.vars.outputs.cache-key }}
inputs:
key-prefix:
description: "Prefix for the cache key"
required: true
cache-path:
description: "Path to the cache directory"
required: true
runs:
using: "composite"
steps:
- name: Get date values and cache key
id: vars
shell: bash
run: |
export YEAR_MONTH=$(date +'%Y-%m')
export PREV_YEAR_MONTH=$(date -d 'last month' +'%Y-%m')
export DAY=$(date +'%d')
echo "year-month=$YEAR_MONTH" >> "$GITHUB_OUTPUT"
echo "prev-year-month=$PREV_YEAR_MONTH" >> "$GITHUB_OUTPUT"
echo "cache-key=${INPUTS_KEY_PREFIX}-${YEAR_MONTH}-${DAY}" >> "$GITHUB_OUTPUT"
env:
INPUTS_KEY_PREFIX: ${{ inputs.key-prefix }}
# By default, depot keeps caches for 14 days. This is plenty for embedded
# postgres, which changes infrequently.
# https://depot.dev/docs/github-actions/overview#cache-retention-policy
- name: Download embedded Postgres cache
uses: actions/cache/restore@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
with:
path: ${{ inputs.cache-path }}
key: ${{ steps.vars.outputs.cache-key }}
# > If there are multiple partial matches for a restore key, the action returns the most recently created cache.
# https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/caching-dependencies-to-speed-up-workflows#matching-a-cache-key
# The second restore key allows non-main branches to use the cache from the previous month.
# This prevents PRs from rebuilding the cache on the first day of the month.
# It also makes sure that once a month, the cache is fully reset.
restore-keys: |
${{ inputs.key-prefix }}-${{ steps.vars.outputs.year-month }}-
${{ github.ref != 'refs/heads/main' && format('{0}-{1}-', inputs.key-prefix, steps.vars.outputs.prev-year-month) || '' }}
@@ -1,18 +0,0 @@
name: "Upload Embedded Postgres Cache"
description: Uploads the embedded Postgres cache. This only runs on the main branch.
inputs:
cache-key:
description: "Cache key"
required: true
cache-path:
description: "Path to the cache directory"
required: true
runs:
using: "composite"
steps:
- name: Upload Embedded Postgres cache
if: ${{ github.ref == 'refs/heads/main' }}
uses: actions/cache/save@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
with:
path: ${{ inputs.cache-path }}
key: ${{ inputs.cache-key }}
@@ -1,10 +0,0 @@
name: "Install cosign"
description: |
Cosign Github Action.
runs:
using: "composite"
steps:
- name: Install cosign
uses: sigstore/cosign-installer@d7d6bc7722e3daa8354c50bcb52f4837da5e9b6a # v3.8.1
with:
cosign-release: "v2.4.3"
-10
View File
@@ -1,10 +0,0 @@
name: "Install syft"
description: |
Downloads Syft to the Action tool cache and provides a reference.
runs:
using: "composite"
steps:
- name: Install syft
uses: anchore/sbom-action/download-syft@f325610c9f50a54015d37c8d16cb3b0e2c8f4de0 # v0.18.0
with:
syft-version: "v1.20.0"
@@ -1,33 +0,0 @@
name: "Setup Embedded Postgres Cache Paths"
description: Sets up a path for cached embedded postgres binaries.
outputs:
embedded-pg-cache:
description: "Value of EMBEDDED_PG_CACHE_DIR"
value: ${{ steps.paths.outputs.embedded-pg-cache }}
cached-dirs:
description: "directories that should be cached between CI runs"
value: ${{ steps.paths.outputs.cached-dirs }}
runs:
using: "composite"
steps:
- name: Override Go paths
id: paths
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7
with:
script: |
const path = require('path');
// RUNNER_TEMP should be backed by a RAM disk on Windows if
// coder/setup-ramdisk-action was used
const runnerTemp = process.env.RUNNER_TEMP;
const embeddedPgCacheDir = path.join(runnerTemp, 'embedded-pg-cache');
core.exportVariable('EMBEDDED_PG_CACHE_DIR', embeddedPgCacheDir);
core.setOutput('embedded-pg-cache', embeddedPgCacheDir);
const cachedDirs = `${embeddedPgCacheDir}`;
core.setOutput('cached-dirs', cachedDirs);
- name: Create directories
shell: bash
run: |
set -e
mkdir -p "$EMBEDDED_PG_CACHE_DIR"
-57
View File
@@ -1,57 +0,0 @@
name: "Setup Go Paths"
description: Overrides Go paths like GOCACHE and GOMODCACHE to use temporary directories.
outputs:
gocache:
description: "Value of GOCACHE"
value: ${{ steps.paths.outputs.gocache }}
gomodcache:
description: "Value of GOMODCACHE"
value: ${{ steps.paths.outputs.gomodcache }}
gopath:
description: "Value of GOPATH"
value: ${{ steps.paths.outputs.gopath }}
gotmp:
description: "Value of GOTMPDIR"
value: ${{ steps.paths.outputs.gotmp }}
cached-dirs:
description: "Go directories that should be cached between CI runs"
value: ${{ steps.paths.outputs.cached-dirs }}
runs:
using: "composite"
steps:
- name: Override Go paths
id: paths
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7
with:
script: |
const path = require('path');
// RUNNER_TEMP should be backed by a RAM disk on Windows if
// coder/setup-ramdisk-action was used
const runnerTemp = process.env.RUNNER_TEMP;
const gocacheDir = path.join(runnerTemp, 'go-cache');
const gomodcacheDir = path.join(runnerTemp, 'go-mod-cache');
const gopathDir = path.join(runnerTemp, 'go-path');
const gotmpDir = path.join(runnerTemp, 'go-tmp');
core.exportVariable('GOCACHE', gocacheDir);
core.exportVariable('GOMODCACHE', gomodcacheDir);
core.exportVariable('GOPATH', gopathDir);
core.exportVariable('GOTMPDIR', gotmpDir);
core.setOutput('gocache', gocacheDir);
core.setOutput('gomodcache', gomodcacheDir);
core.setOutput('gopath', gopathDir);
core.setOutput('gotmp', gotmpDir);
const cachedDirs = `${gocacheDir}\n${gomodcacheDir}`;
core.setOutput('cached-dirs', cachedDirs);
- name: Create directories
shell: bash
run: |
set -e
mkdir -p "$GOCACHE"
mkdir -p "$GOMODCACHE"
mkdir -p "$GOPATH"
mkdir -p "$GOTMPDIR"
@@ -1,14 +0,0 @@
name: "Setup Go tools"
description: |
Set up tools for `make gen`, `offlinedocs` and Schmoder CI.
runs:
using: "composite"
steps:
- name: go install tools
shell: bash
run: |
go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.30
go install storj.io/drpc/cmd/protoc-gen-go-drpc@v0.0.34
go install golang.org/x/tools/cmd/goimports@v0.31.0
go install github.com/mikefarah/yq/v4@v4.44.3
go install go.uber.org/mock/mockgen@v0.5.0
-35
View File
@@ -1,35 +0,0 @@
name: "Setup Go"
description: |
Sets up the Go environment for tests, builds, etc.
inputs:
version:
description: "The Go version to use."
default: "1.24.10"
use-preinstalled-go:
description: "Whether to use preinstalled Go."
default: "false"
use-cache:
description: "Whether to use the cache."
default: "true"
runs:
using: "composite"
steps:
- name: Setup Go
uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2
with:
go-version: ${{ inputs.use-preinstalled-go == 'false' && inputs.version || '' }}
cache: ${{ inputs.use-cache }}
- name: Install gotestsum
shell: bash
run: go install gotest.tools/gotestsum@0d9599e513d70e5792bb9334869f82f6e8b53d4d # main as of 2025-05-15
- name: Install mtimehash
shell: bash
run: go install github.com/slsyy/mtimehash/cmd/mtimehash@a6b5da4ed2c4a40e7b805534b004e9fde7b53ce0 # v1.0.0
# It isn't necessary that we ever do this, but it helps
# separate the "setup" from the "run" times.
- name: go mod download
shell: bash
run: go mod download -x
-31
View File
@@ -1,31 +0,0 @@
name: "Setup Node"
description: |
Sets up the node environment for tests, builds, etc.
inputs:
directory:
description: |
The directory to run the setup in.
required: false
default: "site"
runs:
using: "composite"
steps:
- name: Install pnpm
uses: pnpm/action-setup@fe02b34f77f8bc703788d5817da081398fad5dd2 # v4.0.0
- name: Setup Node
uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4
with:
node-version: 22.19.0
# See https://github.com/actions/setup-node#caching-global-packages-data
cache: "pnpm"
cache-dependency-path: ${{ inputs.directory }}/pnpm-lock.yaml
- name: Install root node_modules
shell: bash
run: ./scripts/pnpm_install.sh
- name: Install node_modules
shell: bash
run: ../scripts/pnpm_install.sh
working-directory: ${{ inputs.directory }}
-17
View File
@@ -1,17 +0,0 @@
name: Setup sqlc
description: |
Sets up the sqlc environment for tests, builds, etc.
runs:
using: "composite"
steps:
- name: Setup sqlc
# uses: sqlc-dev/setup-sqlc@c0209b9199cd1cce6a14fc27cabcec491b651761 # v4.0.0
# with:
# sqlc-version: "1.30.0"
# Switched to coder/sqlc fork to fix ambiguous column bug, see:
# - https://github.com/coder/sqlc/pull/1
# - https://github.com/sqlc-dev/sqlc/pull/4159
shell: bash
run: |
CGO_ENABLED=1 go install github.com/coder/sqlc/cmd/sqlc@aab4e865a51df0c43e1839f81a9d349b41d14f05
-11
View File
@@ -1,11 +0,0 @@
name: "Setup Terraform"
description: |
Sets up Terraform for tests, builds, etc.
runs:
using: "composite"
steps:
- name: Install Terraform
uses: hashicorp/setup-terraform@b9cd54a3c349d3f38e8881555d616ced269862dd # v3.1.2
with:
terraform_version: 1.13.4
terraform_wrapper: false
@@ -1,52 +0,0 @@
name: "Download Test Cache"
description: |
Downloads the test cache and outputs today's cache key.
A PR job can use a cache if it was created by its base branch, its current
branch, or the default branch.
https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/caching-dependencies-to-speed-up-workflows#restrictions-for-accessing-a-cache
outputs:
cache-key:
description: "Today's cache key"
value: ${{ steps.vars.outputs.cache-key }}
inputs:
key-prefix:
description: "Prefix for the cache key"
required: true
cache-path:
description: "Path to the cache directory"
required: true
# This path is defined in testutil/cache.go
default: "~/.cache/coderv2-test"
runs:
using: "composite"
steps:
- name: Get date values and cache key
id: vars
shell: bash
run: |
export YEAR_MONTH=$(date +'%Y-%m')
export PREV_YEAR_MONTH=$(date -d 'last month' +'%Y-%m')
export DAY=$(date +'%d')
echo "year-month=$YEAR_MONTH" >> "$GITHUB_OUTPUT"
echo "prev-year-month=$PREV_YEAR_MONTH" >> "$GITHUB_OUTPUT"
echo "cache-key=${INPUTS_KEY_PREFIX}-${YEAR_MONTH}-${DAY}" >> "$GITHUB_OUTPUT"
env:
INPUTS_KEY_PREFIX: ${{ inputs.key-prefix }}
# TODO: As a cost optimization, we could remove caches that are older than
# a day or two. By default, depot keeps caches for 14 days, which isn't
# necessary for the test cache.
# https://depot.dev/docs/github-actions/overview#cache-retention-policy
- name: Download test cache
uses: actions/cache/restore@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
with:
path: ${{ inputs.cache-path }}
key: ${{ steps.vars.outputs.cache-key }}
# > If there are multiple partial matches for a restore key, the action returns the most recently created cache.
# https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/caching-dependencies-to-speed-up-workflows#matching-a-cache-key
# The second restore key allows non-main branches to use the cache from the previous month.
# This prevents PRs from rebuilding the cache on the first day of the month.
# It also makes sure that once a month, the cache is fully reset.
restore-keys: |
${{ inputs.key-prefix }}-${{ steps.vars.outputs.year-month }}-
${{ github.ref != 'refs/heads/main' && format('{0}-{1}-', inputs.key-prefix, steps.vars.outputs.prev-year-month) || '' }}
@@ -1,20 +0,0 @@
name: "Upload Test Cache"
description: Uploads the test cache. Only works on the main branch.
inputs:
cache-key:
description: "Cache key"
required: true
cache-path:
description: "Path to the cache directory"
required: true
# This path is defined in testutil/cache.go
default: "~/.cache/coderv2-test"
runs:
using: "composite"
steps:
- name: Upload test cache
if: ${{ github.ref == 'refs/heads/main' }}
uses: actions/cache/save@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
with:
path: ${{ inputs.cache-path }}
key: ${{ inputs.cache-key }}
@@ -1,67 +0,0 @@
name: Upload tests to datadog
description: |
Uploads the test results to datadog.
inputs:
api-key:
description: "Datadog API key"
required: true
runs:
using: "composite"
steps:
- shell: bash
run: |
set -e
echo "owner: $REPO_OWNER"
if [[ "$REPO_OWNER" != "coder" ]]; then
echo "Not a pull request from the main repo, skipping..."
exit 0
fi
if [[ -z "${DATADOG_API_KEY}" ]]; then
# This can happen for dependabot.
echo "No API key provided, skipping..."
exit 0
fi
BINARY_VERSION="v2.48.0"
BINARY_HASH_WINDOWS="b7bebb8212403fddb1563bae84ce5e69a70dac11e35eb07a00c9ef7ac9ed65ea"
BINARY_HASH_MACOS="e87c808638fddb21a87a5c4584b68ba802965eb0a593d43959c81f67246bd9eb"
BINARY_HASH_LINUX="5e700c465728fff8313e77c2d5ba1ce19a736168735137e1ddc7c6346ed48208"
TMP_DIR=$(mktemp -d)
if [[ "${RUNNER_OS}" == "Windows" ]]; then
BINARY_PATH="${TMP_DIR}/datadog-ci.exe"
BINARY_URL="https://github.com/DataDog/datadog-ci/releases/download/${BINARY_VERSION}/datadog-ci_win-x64"
elif [[ "${RUNNER_OS}" == "macOS" ]]; then
BINARY_PATH="${TMP_DIR}/datadog-ci"
BINARY_URL="https://github.com/DataDog/datadog-ci/releases/download/${BINARY_VERSION}/datadog-ci_darwin-arm64"
elif [[ "${RUNNER_OS}" == "Linux" ]]; then
BINARY_PATH="${TMP_DIR}/datadog-ci"
BINARY_URL="https://github.com/DataDog/datadog-ci/releases/download/${BINARY_VERSION}/datadog-ci_linux-x64"
else
echo "Unsupported OS: $RUNNER_OS"
exit 1
fi
echo "Downloading DataDog CI binary version ${BINARY_VERSION} for $RUNNER_OS..."
curl -sSL "$BINARY_URL" -o "$BINARY_PATH"
if [[ "${RUNNER_OS}" == "Windows" ]]; then
echo "$BINARY_HASH_WINDOWS $BINARY_PATH" | sha256sum --check
elif [[ "${RUNNER_OS}" == "macOS" ]]; then
echo "$BINARY_HASH_MACOS $BINARY_PATH" | shasum -a 256 --check
elif [[ "${RUNNER_OS}" == "Linux" ]]; then
echo "$BINARY_HASH_LINUX $BINARY_PATH" | sha256sum --check
fi
# Make binary executable (not needed for Windows)
if [[ "${RUNNER_OS}" != "Windows" ]]; then
chmod +x "$BINARY_PATH"
fi
"$BINARY_PATH" junit upload --service coder ./gotests.xml \
--tags "os:${RUNNER_OS}" --tags "runner_name:${RUNNER_NAME}"
env:
REPO_OWNER: ${{ github.repository_owner }}
DATADOG_API_KEY: ${{ inputs.api-key }}
-2
View File
@@ -1,2 +0,0 @@
enabled: true
preservePullRequestTitle: true
-131
View File
@@ -1,131 +0,0 @@
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
time: "06:00"
timezone: "America/Chicago"
cooldown:
default-days: 7
labels: []
commit-message:
prefix: "ci"
groups:
github-actions:
patterns:
- "*"
- package-ecosystem: "gomod"
directory: "/"
schedule:
interval: "weekly"
time: "06:00"
timezone: "America/Chicago"
commit-message:
prefix: "chore"
labels: []
open-pull-requests-limit: 15
groups:
x:
patterns:
- "golang.org/x/*"
ignore:
# Ignore patch updates for all dependencies
- dependency-name: "*"
update-types:
- version-update:semver-patch
- dependency-name: "github.com/mark3labs/mcp-go"
# Update our Dockerfile.
- package-ecosystem: "docker"
directories:
- "/dogfood/coder"
- "/dogfood/coder-envbuilder"
- "/scripts"
- "/examples/templates/docker/build"
- "/examples/parameters/build"
- "/scaletest/templates/scaletest-runner"
- "/scripts/ironbank"
schedule:
interval: "weekly"
time: "06:00"
timezone: "America/Chicago"
commit-message:
prefix: "chore"
labels: []
ignore:
# We need to coordinate terraform updates with the version hardcoded in
# our Go code.
- dependency-name: "terraform"
- package-ecosystem: "npm"
directories:
- "/site"
- "/offlinedocs"
- "/scripts"
- "/scripts/apidocgen"
schedule:
interval: "monthly"
time: "06:00"
timezone: "America/Chicago"
cooldown:
default-days: 7
commit-message:
prefix: "chore"
labels: []
groups:
xterm:
patterns:
- "@xterm*"
mui:
patterns:
- "@mui*"
radix:
patterns:
- "@radix-ui/*"
react:
patterns:
- "react"
- "react-dom"
- "@types/react"
- "@types/react-dom"
emotion:
patterns:
- "@emotion*"
exclude-patterns:
- "jest-runner-eslint"
jest:
patterns:
- "jest"
- "@types/jest"
vite:
patterns:
- "vite*"
- "@vitejs/plugin-react"
ignore:
# Ignore major version updates to avoid breaking changes
- dependency-name: "*"
update-types:
- version-update:semver-major
- dependency-name: "@playwright/test"
open-pull-requests-limit: 15
- package-ecosystem: "terraform"
directories:
- "dogfood/*/"
- "examples/templates/*/"
schedule:
interval: "weekly"
commit-message:
prefix: "chore"
groups:
coder-modules:
patterns:
- "coder/*/coder"
labels: []
ignore:
- dependency-name: "*"
update-types:
- version-update:semver-major
+49
View File
@@ -0,0 +1,49 @@
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "daily"
time: "06:00"
timezone: "America/Chicago"
commit-message:
prefix: "chore"
ignore:
# These actions deliver the latest versions by updating the major
# release tag, so ignore minor and patch versions
- dependency-name: "actions/*"
update-types:
- version-update:semver-minor
- version-update:semver-patch
- dependency-name: "Apple-Actions/import-codesign-certs"
update-types:
- version-update:semver-minor
- version-update:semver-patch
- dependency-name: "marocchino/sticky-pull-request-comment"
update-types:
- version-update:semver-minor
- version-update:semver-patch
- package-ecosystem: "gomod"
directory: "/"
schedule:
interval: "daily"
time: "06:00"
timezone: "America/Chicago"
commit-message:
prefix: "chore"
- package-ecosystem: "npm"
directory: "/site/"
schedule:
interval: "daily"
time: "06:00"
timezone: "America/Chicago"
commit-message:
prefix: "chore"
ignore:
# Ignore major updates to Node.js types, because they need to
# correspond to the Node.js engine version
- dependency-name: "@types/node"
update-types:
- version-update:semver-major
-34
View File
@@ -1,34 +0,0 @@
app = "jnb-coder"
primary_region = "jnb"
[experimental]
entrypoint = ["/bin/sh", "-c", "CODER_DERP_SERVER_RELAY_URL=\"http://[${FLY_PRIVATE_IP}]:3000\" /opt/coder wsproxy server"]
auto_rollback = true
[build]
image = "ghcr.io/coder/coder-preview:main"
[env]
CODER_ACCESS_URL = "https://jnb.fly.dev.coder.com"
CODER_HTTP_ADDRESS = "0.0.0.0:3000"
CODER_PRIMARY_ACCESS_URL = "https://dev.coder.com"
CODER_WILDCARD_ACCESS_URL = "*--apps.jnb.fly.dev.coder.com"
CODER_VERBOSE = "true"
[http_service]
internal_port = 3000
force_https = true
auto_stop_machines = true
auto_start_machines = true
min_machines_running = 0
# Ref: https://fly.io/docs/reference/configuration/#http_service-concurrency
[http_service.concurrency]
type = "requests"
soft_limit = 50
hard_limit = 100
[[vm]]
cpu_kind = "shared"
cpus = 2
memory_mb = 512
-34
View File
@@ -1,34 +0,0 @@
app = "paris-coder"
primary_region = "cdg"
[experimental]
entrypoint = ["/bin/sh", "-c", "CODER_DERP_SERVER_RELAY_URL=\"http://[${FLY_PRIVATE_IP}]:3000\" /opt/coder wsproxy server"]
auto_rollback = true
[build]
image = "ghcr.io/coder/coder-preview:main"
[env]
CODER_ACCESS_URL = "https://paris.fly.dev.coder.com"
CODER_HTTP_ADDRESS = "0.0.0.0:3000"
CODER_PRIMARY_ACCESS_URL = "https://dev.coder.com"
CODER_WILDCARD_ACCESS_URL = "*--apps.paris.fly.dev.coder.com"
CODER_VERBOSE = "true"
[http_service]
internal_port = 3000
force_https = true
auto_stop_machines = true
auto_start_machines = true
min_machines_running = 0
# Ref: https://fly.io/docs/reference/configuration/#http_service-concurrency
[http_service.concurrency]
type = "requests"
soft_limit = 50
hard_limit = 100
[[vm]]
cpu_kind = "shared"
cpus = 2
memory_mb = 512
-34
View File
@@ -1,34 +0,0 @@
app = "sydney-coder"
primary_region = "syd"
[experimental]
entrypoint = ["/bin/sh", "-c", "CODER_DERP_SERVER_RELAY_URL=\"http://[${FLY_PRIVATE_IP}]:3000\" /opt/coder wsproxy server"]
auto_rollback = true
[build]
image = "ghcr.io/coder/coder-preview:main"
[env]
CODER_ACCESS_URL = "https://sydney.fly.dev.coder.com"
CODER_HTTP_ADDRESS = "0.0.0.0:3000"
CODER_PRIMARY_ACCESS_URL = "https://dev.coder.com"
CODER_WILDCARD_ACCESS_URL = "*--apps.sydney.fly.dev.coder.com"
CODER_VERBOSE = "true"
[http_service]
internal_port = 3000
force_https = true
auto_stop_machines = true
auto_start_machines = true
min_machines_running = 0
# Ref: https://fly.io/docs/reference/configuration/#http_service-concurrency
[http_service.concurrency]
type = "requests"
soft_limit = 50
hard_limit = 100
[[vm]]
cpu_kind = "shared"
cpus = 2
memory_mb = 512
-13
View File
@@ -1,13 +0,0 @@
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: pr${PR_NUMBER}-tls
namespace: pr-deployment-certs
spec:
secretName: pr${PR_NUMBER}-tls
issuerRef:
name: letsencrypt
kind: ClusterIssuer
dnsNames:
- "${PR_HOSTNAME}"
- "*.${PR_HOSTNAME}"
-31
View File
@@ -1,31 +0,0 @@
apiVersion: v1
kind: ServiceAccount
metadata:
name: coder-workspace-pr${PR_NUMBER}
namespace: pr${PR_NUMBER}
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: coder-workspace-pr${PR_NUMBER}
namespace: pr${PR_NUMBER}
rules:
- apiGroups: ["*"]
resources: ["*"]
verbs: ["*"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: coder-workspace-pr${PR_NUMBER}
namespace: pr${PR_NUMBER}
subjects:
- kind: ServiceAccount
name: coder-workspace-pr${PR_NUMBER}
namespace: pr${PR_NUMBER}
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: coder-workspace-pr${PR_NUMBER}
-314
View File
@@ -1,314 +0,0 @@
terraform {
required_providers {
coder = {
source = "coder/coder"
}
kubernetes = {
source = "hashicorp/kubernetes"
}
}
}
provider "coder" {
}
variable "namespace" {
type = string
description = "The Kubernetes namespace to create workspaces in (must exist prior to creating workspaces)"
}
data "coder_parameter" "cpu" {
name = "cpu"
display_name = "CPU"
description = "The number of CPU cores"
default = "2"
icon = "/icon/memory.svg"
mutable = true
option {
name = "2 Cores"
value = "2"
}
option {
name = "4 Cores"
value = "4"
}
option {
name = "6 Cores"
value = "6"
}
option {
name = "8 Cores"
value = "8"
}
}
data "coder_parameter" "memory" {
name = "memory"
display_name = "Memory"
description = "The amount of memory in GB"
default = "2"
icon = "/icon/memory.svg"
mutable = true
option {
name = "2 GB"
value = "2"
}
option {
name = "4 GB"
value = "4"
}
option {
name = "6 GB"
value = "6"
}
option {
name = "8 GB"
value = "8"
}
}
data "coder_parameter" "home_disk_size" {
name = "home_disk_size"
display_name = "Home disk size"
description = "The size of the home disk in GB"
default = "10"
type = "number"
icon = "/emojis/1f4be.png"
mutable = false
validation {
min = 1
max = 99999
}
}
provider "kubernetes" {
config_path = null
}
data "coder_workspace" "me" {}
data "coder_workspace_owner" "me" {}
resource "coder_agent" "main" {
os = "linux"
arch = "amd64"
startup_script = <<-EOT
set -e
# install and start code-server
curl -fsSL https://code-server.dev/install.sh | sh -s -- --method=standalone --prefix=/tmp/code-server
/tmp/code-server/bin/code-server --auth none --port 13337 >/tmp/code-server.log 2>&1 &
EOT
# The following metadata blocks are optional. They are used to display
# information about your workspace in the dashboard. You can remove them
# if you don't want to display any information.
# For basic resources, you can use the `coder stat` command.
# If you need more control, you can write your own script.
metadata {
display_name = "CPU Usage"
key = "0_cpu_usage"
script = "coder stat cpu"
interval = 10
timeout = 1
}
metadata {
display_name = "RAM Usage"
key = "1_ram_usage"
script = "coder stat mem"
interval = 10
timeout = 1
}
metadata {
display_name = "Home Disk"
key = "3_home_disk"
script = "coder stat disk --path $${HOME}"
interval = 60
timeout = 1
}
metadata {
display_name = "CPU Usage (Host)"
key = "4_cpu_usage_host"
script = "coder stat cpu --host"
interval = 10
timeout = 1
}
metadata {
display_name = "Memory Usage (Host)"
key = "5_mem_usage_host"
script = "coder stat mem --host"
interval = 10
timeout = 1
}
metadata {
display_name = "Load Average (Host)"
key = "6_load_host"
# get load avg scaled by number of cores
script = <<EOT
echo "`cat /proc/loadavg | awk '{ print $1 }'` `nproc`" | awk '{ printf "%0.2f", $1/$2 }'
EOT
interval = 60
timeout = 1
}
}
# code-server
resource "coder_app" "code-server" {
agent_id = coder_agent.main.id
slug = "code-server"
display_name = "code-server"
icon = "/icon/code.svg"
url = "http://localhost:13337?folder=/home/coder"
subdomain = false
share = "owner"
healthcheck {
url = "http://localhost:13337/healthz"
interval = 3
threshold = 10
}
}
resource "kubernetes_persistent_volume_claim" "home" {
metadata {
name = "coder-${lower(data.coder_workspace_owner.me.name)}-${lower(data.coder_workspace.me.name)}-home"
namespace = var.namespace
labels = {
"app.kubernetes.io/name" = "coder-pvc"
"app.kubernetes.io/instance" = "coder-pvc-${lower(data.coder_workspace_owner.me.name)}-${lower(data.coder_workspace.me.name)}"
"app.kubernetes.io/part-of" = "coder"
//Coder-specific labels.
"com.coder.resource" = "true"
"com.coder.workspace.id" = data.coder_workspace.me.id
"com.coder.workspace.name" = data.coder_workspace.me.name
"com.coder.user.id" = data.coder_workspace_owner.me.id
"com.coder.user.username" = data.coder_workspace_owner.me.name
}
annotations = {
"com.coder.user.email" = data.coder_workspace_owner.me.email
}
}
wait_until_bound = false
spec {
access_modes = ["ReadWriteOnce"]
resources {
requests = {
storage = "${data.coder_parameter.home_disk_size.value}Gi"
}
}
}
}
resource "kubernetes_deployment" "main" {
count = data.coder_workspace.me.start_count
depends_on = [
kubernetes_persistent_volume_claim.home
]
wait_for_rollout = false
metadata {
name = "coder-${lower(data.coder_workspace_owner.me.name)}-${lower(data.coder_workspace.me.name)}"
namespace = var.namespace
labels = {
"app.kubernetes.io/name" = "coder-workspace"
"app.kubernetes.io/instance" = "coder-workspace-${lower(data.coder_workspace_owner.me.name)}-${lower(data.coder_workspace.me.name)}"
"app.kubernetes.io/part-of" = "coder"
"com.coder.resource" = "true"
"com.coder.workspace.id" = data.coder_workspace.me.id
"com.coder.workspace.name" = data.coder_workspace.me.name
"com.coder.user.id" = data.coder_workspace_owner.me.id
"com.coder.user.username" = data.coder_workspace_owner.me.name
}
annotations = {
"com.coder.user.email" = data.coder_workspace_owner.me.email
}
}
spec {
replicas = 1
selector {
match_labels = {
"app.kubernetes.io/name" = "coder-workspace"
}
}
strategy {
type = "Recreate"
}
template {
metadata {
labels = {
"app.kubernetes.io/name" = "coder-workspace"
}
}
spec {
security_context {
run_as_user = 1000
fs_group = 1000
}
service_account_name = "coder-workspace-${var.namespace}"
container {
name = "dev"
image = "bencdr/devops-tools"
image_pull_policy = "Always"
command = ["sh", "-c", coder_agent.main.init_script]
security_context {
run_as_user = "1000"
}
env {
name = "CODER_AGENT_TOKEN"
value = coder_agent.main.token
}
resources {
requests = {
"cpu" = "250m"
"memory" = "512Mi"
}
limits = {
"cpu" = "${data.coder_parameter.cpu.value}"
"memory" = "${data.coder_parameter.memory.value}Gi"
}
}
volume_mount {
mount_path = "/home/coder"
name = "home"
read_only = false
}
}
volume {
name = "home"
persistent_volume_claim {
claim_name = kubernetes_persistent_volume_claim.home.metadata.0.name
read_only = false
}
}
affinity {
// This affinity attempts to spread out all workspace pods evenly across
// nodes.
pod_anti_affinity {
preferred_during_scheduling_ignored_during_execution {
weight = 1
pod_affinity_term {
topology_key = "kubernetes.io/hostname"
label_selector {
match_expressions {
key = "app.kubernetes.io/name"
operator = "In"
values = ["coder-workspace"]
}
}
}
}
}
}
}
}
}
}
-38
View File
@@ -1,38 +0,0 @@
coder:
image:
repo: "${REPO}"
tag: "pr${PR_NUMBER}"
pullPolicy: Always
service:
type: ClusterIP
ingress:
enable: true
className: traefik
host: "${PR_HOSTNAME}"
wildcardHost: "*.${PR_HOSTNAME}"
tls:
enable: true
secretName: "pr${PR_NUMBER}-tls"
wildcardSecretName: "pr${PR_NUMBER}-tls"
env:
- name: "CODER_ACCESS_URL"
value: "https://${PR_HOSTNAME}"
- name: "CODER_WILDCARD_ACCESS_URL"
value: "*.${PR_HOSTNAME}"
- name: "CODER_EXPERIMENTS"
value: "${EXPERIMENTS}"
- name: CODER_PG_CONNECTION_URL
valueFrom:
secretKeyRef:
name: coder-db-url
key: url
- name: "CODER_OAUTH2_GITHUB_ALLOW_SIGNUPS"
value: "true"
- name: "CODER_OAUTH2_GITHUB_CLIENT_ID"
value: "${PR_DEPLOYMENTS_GITHUB_OAUTH_CLIENT_ID}"
- name: "CODER_OAUTH2_GITHUB_CLIENT_SECRET"
value: "${PR_DEPLOYMENTS_GITHUB_OAUTH_CLIENT_SECRET}"
- name: "CODER_OAUTH2_GITHUB_ALLOWED_ORGS"
value: "coder"
- name: "CODER_DERP_CONFIG_URL"
value: "https://controlplane.tailscale.com/derpmap/default"
-5
View File
@@ -1,5 +0,0 @@
<!--
If you have used AI to produce some or all of this PR, please ensure you have read our [AI Contribution guidelines](https://coder.com/docs/about/contributing/AI_CONTRIBUTING) before submitting.
-->
+56
View File
@@ -0,0 +1,56 @@
###############################################################################
# This file configures "Semantic Pull Requests", which is documented here:
# https://github.com/zeke/semantic-pull-requests
#
# This action/spec implements the "Conventional Commits" RFC which is
# available here:
# https://www.notion.so/coderhq/Conventional-commits-1d51287f58b64026bb29393f277734ed
###############################################################################
# We only check that the PR title is semantic. The PR title is automatically
# applied to the "Squash & Merge" flow as the suggested commit message, so this
# should suffice unless someone drastically alters the message in that flow.
titleOnly: true
# Types are the 'tag' types in a commit or PR title. For example, in
#
# chore: fix thing
#
# 'chore' is the type.
types:
# A build of any kind.
- build
# A RELEASED fix that will NOT be back-ported. The originating issue may have
# been discovered internally or externally to Coder.
- fix
# Any code task that is ignored for changelog purposes. Examples include
# devbin scripts and internal-only configurations.
- chore
# Any work performed on CI.
- ci
# An UNRELEASED correction. For example, features are often built
# incrementally and sometimes introduce minor flaws during a release cycle.
# Corrections address those increments and flaws.
- correct
# Work that directly implements or supports the implementation of a feature.
- feat
# A fix for a RELEASED bug (regression fix) that is intended for patch-release
# purposes.
- hotfix
# A refactor changes code structure without any behavioral change.
- refactor
# A git revert for any style of commit.
- revert
# Adding tests of any kind. Should be separate from feature or fix
# implementations. For example, if a commit adds a fix + test, it's a fix
# commit. If a commit is simply bumping coverage, it's a test commit.
- test
+14
View File
@@ -0,0 +1,14 @@
# Number of days of inactivity before an issue becomes stale
daysUntilStale: 14
# Number of days of inactivity before a stale issue is closed
daysUntilClose: 5
# Only apply the stale logic to pulls, since we are using issues to manage work
only: pulls
# Label to apply when stale.
staleLabel: stale
# Comment to post when marking an issue as stale. Set to `false` to disable
markComment: >
This issue has been automatically marked as stale because it has not had
recent activity. It will be closed if no activity occurs in the next 5 days.
# Comment to post when closing a stale issue. Set to `false` to disable
closeComment: false
File diff suppressed because it is too large Load Diff
+373
View File
@@ -0,0 +1,373 @@
name: coder
on:
push:
branches:
- main
- "release/*"
tags:
- "*"
pull_request:
branches:
- "*"
workflow_dispatch:
permissions:
actions: none
checks: none
contents: read
deployments: none
issues: none
packages: none
pull-requests: none
repository-projects: none
security-events: none
statuses: none
# Cancel in-progress runs for pull requests when developers push
# additional changes
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
jobs:
style-lint-golangci:
name: style/lint/golangci
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-go@v2
with:
go-version: "^1.17"
- name: golangci-lint
uses: golangci/golangci-lint-action@v3.1.0
with:
version: v1.43.0
style-lint-typescript:
name: "style/lint/typescript"
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Cache Node
id: cache-node
uses: actions/cache@v2
with:
path: |
**/node_modules
.eslintcache
key: js-${{ runner.os }}-test-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
js-${{ runner.os }}-
- name: Install node_modules
run: ./scripts/yarn_install.sh
- name: "yarn lint"
run: yarn lint
working-directory: site
gen:
name: "style/gen"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install Protoc
uses: arduino/setup-protoc@v1
with:
version: "3.6.1"
- uses: actions/setup-go@v2
with:
go-version: "^1.17"
- run: curl -sSL
https://github.com/kyleconroy/sqlc/releases/download/v1.11.0/sqlc_1.11.0_linux_amd64.tar.gz
| sudo tar -C /usr/bin -xz sqlc
- run: go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.26
- run: go install storj.io/drpc/cmd/protoc-gen-go-drpc@v0.0.26
- run: "make --output-sync -j gen"
- run: ./scripts/check_unstaged.sh
style-fmt:
name: "style/fmt"
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
with:
fetch-depth: 0
submodules: true
- name: Cache Node
id: cache-node
uses: actions/cache@v2
with:
path: |
**/node_modules
.eslintcache
key: js-${{ runner.os }}-test-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
js-${{ runner.os }}-
- name: Install node_modules
run: ./scripts/yarn_install.sh
- name: "make fmt"
run: "make --output-sync -j fmt"
test-go:
name: "test/go"
runs-on: ${{ matrix.os }}
strategy:
matrix:
os:
- ubuntu-latest
- macos-latest
- windows-2022
steps:
- uses: actions/checkout@v3
- uses: actions/setup-go@v2
with:
go-version: "^1.17"
- uses: actions/cache@v2
with:
# Go mod cache, Linux build cache, Mac build cache, Windows build cache
path: |
~/go/pkg/mod
~/.cache/go-build
~/Library/Caches/go-build
%LocalAppData%\go-build
key: ${{ matrix.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ matrix.os }}-go-
- run: go install gotest.tools/gotestsum@latest
- uses: hashicorp/setup-terraform@v1
with:
terraform_version: 1.1.2
terraform_wrapper: false
- name: Test with Mock Database
shell: bash
env:
GOCOUNT: ${{ runner.os == 'Windows' && 1 || 2 }}
GOMAXPROCS: ${{ runner.os == 'Windows' && 1 || 2 }}
run: gotestsum --junitfile="gotests.xml" --packages="./..." --
-covermode=atomic -coverprofile="gotests.coverage"
-coverpkg=./...,github.com/coder/coder/codersdk
-timeout=3m -count=$GOCOUNT -race -short -failfast
- name: Upload DataDog Trace
if: (success() || failure()) && github.actor != 'dependabot[bot]'
env:
DATADOG_API_KEY: ${{ secrets.DATADOG_API_KEY }}
DD_DATABASE: fake
DD_CATEGORY: unit
GIT_COMMIT_MESSAGE: ${{ github.event.head_commit.message }}
run: go run scripts/datadog-cireport/main.go gotests.xml
- name: Test with PostgreSQL Database
if: runner.os == 'Linux'
run: DB=true gotestsum --junitfile="gotests.xml" --packages="./..." --
-covermode=atomic -coverprofile="gotests.coverage" -timeout=3m
-coverpkg=./...,github.com/coder/coder/codersdk
-count=1 -parallel=2 -failfast
- name: Upload DataDog Trace
if: (success() || failure()) && github.actor != 'dependabot[bot]' && runner.os == 'Linux'
env:
DATADOG_API_KEY: ${{ secrets.DATADOG_API_KEY }}
DD_DATABASE: postgresql
GIT_COMMIT_MESSAGE: ${{ github.event.head_commit.message }}
run: go run scripts/datadog-cireport/main.go gotests.xml
- uses: codecov/codecov-action@v2
if: github.actor != 'dependabot[bot]'
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: ./gotests.coverage
flags: unittest-go-${{ matrix.os }}
fail_ci_if_error: true
deploy:
name: "deploy"
runs-on: ubuntu-latest
if: github.event_name != 'pull_request'
permissions:
contents: read
id-token: write
steps:
- uses: actions/checkout@v3
- name: Authenticate to Google Cloud
uses: google-github-actions/auth@v0
with:
workload_identity_provider: projects/477254869654/locations/global/workloadIdentityPools/github/providers/github
service_account: github-coder@coder-ci.iam.gserviceaccount.com
- name: Set up Google Cloud SDK
uses: google-github-actions/setup-gcloud@v0
- name: Configure Docker for Google Artifact Registry
run: gcloud auth configure-docker us-docker.pkg.dev
- uses: actions/setup-node@v3
with:
node-version: "14"
- name: Install node_modules
run: ./scripts/yarn_install.sh
- uses: actions/setup-go@v2
with:
go-version: "^1.17"
- uses: goreleaser/goreleaser-action@v2
with:
install-only: true
- run: make docker/image/coder
- run: docker push us-docker.pkg.dev/coder-blacktriangle-dev/ci/coder:latest
- name: Update coder service
run: gcloud run services update coder --image us-docker.pkg.dev/coder-blacktriangle-dev/ci/coder:latest --project coder-blacktriangle-dev --tag "git-$(git rev-parse --short HEAD)" --region us-central1
test-js:
name: "test/js"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Cache Node
id: cache-node
uses: actions/cache@v2
with:
path: |
**/node_modules
.eslintcache
key: js-${{ runner.os }}-test-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
js-${{ runner.os }}-
# Go is required for uploading the test results to datadog
- uses: actions/setup-go@v2
with:
go-version: "^1.17"
- uses: actions/setup-node@v3
with:
node-version: "14"
- name: Install node_modules
run: ./scripts/yarn_install.sh
- name: Build frontend
run: yarn build
working-directory: site
- name: Build Storybook
run: yarn storybook:build
working-directory: site
- run: yarn test:coverage
working-directory: site
- uses: codecov/codecov-action@v2
if: github.actor != 'dependabot[bot]'
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: ./site/coverage/lcov.info
flags: unittest-js
fail_ci_if_error: true
- name: Upload DataDog Trace
if: (success() || failure()) && github.actor != 'dependabot[bot]'
env:
DATADOG_API_KEY: ${{ secrets.DATADOG_API_KEY }}
DD_CATEGORY: unit
GIT_COMMIT_MESSAGE: ${{ github.event.head_commit.message }}
run: go run scripts/datadog-cireport/main.go site/test-results/junit.xml
test-e2e:
name: "test/e2e/${{ matrix.os }}"
runs-on: ${{ matrix.os }}
strategy:
matrix:
os:
- ubuntu-latest
- macos-latest
# TODO: Get `make build` running on Windows 2022
# https://github.com/coder/coder/issues/384
# - windows-2022
steps:
- uses: actions/checkout@v2
- name: Cache Node
id: cache-node
uses: actions/cache@v2
with:
path: |
**/node_modules
.eslintcache
key: js-${{ runner.os }}-test-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
js-${{ runner.os }}-
# Go is required for uploading the test results to datadog
- uses: actions/setup-go@v2
with:
go-version: "^1.17"
- uses: hashicorp/setup-terraform@v1
with:
terraform_version: 1.1.2
terraform_wrapper: false
- uses: actions/setup-node@v3
with:
node-version: "14"
- uses: goreleaser/goreleaser-action@v2
with:
install-only: true
- uses: actions/cache@v2
with:
# Go mod cache, Linux build cache, Mac build cache, Windows build cache
path: |
~/go/pkg/mod
~/.cache/go-build
~/Library/Caches/go-build
%LocalAppData%\go-build
key: ${{ matrix.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ matrix.os }}-go-
- run: make build
- run: yarn playwright:install
working-directory: site
- run: yarn playwright:install-deps
working-directory: site
- run: yarn playwright:test
env:
DEBUG: pw:api
working-directory: site
- name: Upload DataDog Trace
if: (success() || failure()) && github.actor != 'dependabot[bot]' && runner.os == 'Linux'
env:
DATADOG_API_KEY: ${{ secrets.DATADOG_API_KEY }}
DD_CATEGORY: e2e
GIT_COMMIT_MESSAGE: ${{ github.event.head_commit.message }}
run: go run scripts/datadog-cireport/main.go site/test-results/junit.xml
-157
View File
@@ -1,157 +0,0 @@
name: contrib
on:
issue_comment:
types: [created, edited]
# zizmor: ignore[dangerous-triggers] We explicitly want to run on pull_request_target.
pull_request_target:
types:
- opened
- closed
- synchronize
- labeled
- unlabeled
- reopened
- edited
# For jobs that don't run on draft PRs.
- ready_for_review
permissions:
contents: read
# Only run one instance per PR to ensure in-order execution.
concurrency: pr-${{ github.ref }}
jobs:
cla:
runs-on: ubuntu-latest
permissions:
pull-requests: write
steps:
- name: cla
if: (github.event.comment.body == 'recheck' || github.event.comment.body == 'I have read the CLA Document and I hereby sign the CLA') || github.event_name == 'pull_request_target'
uses: contributor-assistant/github-action@ca4a40a7d1004f18d9960b404b97e5f30a505a08 # v2.6.1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# the below token should have repo scope and must be manually added by you in the repository's secret
PERSONAL_ACCESS_TOKEN: ${{ secrets.CDRCI2_GITHUB_TOKEN }}
with:
remote-organization-name: "coder"
remote-repository-name: "cla"
path-to-signatures: "v2022-09-04/signatures.json"
path-to-document: "https://github.com/coder/cla/blob/main/README.md"
# branch should not be protected
branch: "main"
# Some users have signed a corporate CLA with Coder so are exempt from signing our community one.
allowlist: "coryb,aaronlehmann,dependabot*,blink-so*"
release-labels:
runs-on: ubuntu-latest
permissions:
pull-requests: write
# Skip tagging for draft PRs.
if: ${{ github.event_name == 'pull_request_target' && !github.event.pull_request.draft }}
steps:
- name: release-labels
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
with:
# This script ensures PR title and labels are in sync:
#
# When release/breaking label is:
# - Added, rename PR title to include ! (e.g. feat!:)
# - Removed, rename PR title to strip ! (e.g. feat:)
#
# When title is:
# - Renamed (+!), add the release/breaking label
# - Renamed (-!), remove the release/breaking label
script: |
const releaseLabels = {
breaking: "release/breaking",
}
const { action, changes, label, pull_request } = context.payload
const { title } = pull_request
const labels = pull_request.labels.map((label) => label.name)
const isBreakingTitle = isBreaking(title)
// Debug information.
console.log("Action: %s", action)
console.log("Title: %s", title)
console.log("Labels: %s", labels.join(", "))
const params = {
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
}
if (action === "opened" || action === "reopened" || action === "ready_for_review") {
if (isBreakingTitle && !labels.includes(releaseLabels.breaking)) {
console.log('Add "%s" label', releaseLabels.breaking)
await github.rest.issues.addLabels({
...params,
labels: [releaseLabels.breaking],
})
}
}
if (action === "edited" && changes.title) {
if (isBreakingTitle && !labels.includes(releaseLabels.breaking)) {
console.log('Add "%s" label', releaseLabels.breaking)
await github.rest.issues.addLabels({
...params,
labels: [releaseLabels.breaking],
})
}
if (!isBreakingTitle && labels.includes(releaseLabels.breaking)) {
const wasBreakingTitle = isBreaking(changes.title.from)
if (wasBreakingTitle) {
console.log('Remove "%s" label', releaseLabels.breaking)
await github.rest.issues.removeLabel({
...params,
name: releaseLabels.breaking,
})
} else {
console.log('Rename title from "%s" to "%s"', title, toBreaking(title))
await github.rest.issues.update({
...params,
title: toBreaking(title),
})
}
}
}
if (action === "labeled") {
if (label.name === releaseLabels.breaking && !isBreakingTitle) {
console.log('Rename title from "%s" to "%s"', title, toBreaking(title))
await github.rest.issues.update({
...params,
title: toBreaking(title),
})
}
}
if (action === "unlabeled") {
if (label.name === releaseLabels.breaking && isBreakingTitle) {
console.log('Rename title from "%s" to "%s"', title, fromBreaking(title))
await github.rest.issues.update({
...params,
title: fromBreaking(title),
})
}
}
function isBreaking(t) {
return t.split(" ")[0].endsWith("!:")
}
function toBreaking(t) {
const parts = t.split(" ")
return [parts[0].replace(/:$/, "!:"), ...parts.slice(1)].join(" ")
}
function fromBreaking(t) {
const parts = t.split(" ")
return [parts[0].replace(/!:$/, ":"), ...parts.slice(1)].join(" ")
}
-97
View File
@@ -1,97 +0,0 @@
name: dependabot
on:
pull_request:
types:
- opened
permissions:
contents: read
jobs:
dependabot-automerge:
runs-on: ubuntu-latest
if: >
github.event_name == 'pull_request' &&
github.event.action == 'opened' &&
github.event.pull_request.user.login == 'dependabot[bot]' &&
github.event.pull_request.user.id == 49699333 &&
github.repository == 'coder/coder'
permissions:
pull-requests: write
contents: write
steps:
- name: Dependabot metadata
id: metadata
uses: dependabot/fetch-metadata@08eff52bf64351f401fb50d4972fa95b9f2c2d1b # v2.4.0
with:
github-token: "${{ secrets.GITHUB_TOKEN }}"
- name: Approve the PR
if: steps.metadata.outputs.package-ecosystem != 'github-actions'
run: |
echo "Approving $PR_URL"
gh pr review --approve "$PR_URL"
env:
PR_URL: ${{github.event.pull_request.html_url}}
GH_TOKEN: ${{secrets.GITHUB_TOKEN}}
- name: Enable auto-merge
if: steps.metadata.outputs.package-ecosystem != 'github-actions'
run: |
echo "Enabling auto-merge for $PR_URL"
gh pr merge --auto --squash "$PR_URL"
env:
PR_URL: ${{github.event.pull_request.html_url}}
GH_TOKEN: ${{secrets.GITHUB_TOKEN}}
- name: Send Slack notification
run: |
if [ "$PACKAGE_ECOSYSTEM" = "github-actions" ]; then
STATUS_TEXT=":pr-opened: Dependabot opened PR #${PR_NUMBER} (GitHub Actions changes are not auto-merged)"
else
STATUS_TEXT=":pr-merged: Auto merge enabled for Dependabot PR #${PR_NUMBER}"
fi
curl -X POST -H 'Content-type: application/json' \
--data '{
"username": "dependabot",
"icon_url": "https://avatars.githubusercontent.com/u/27347476",
"blocks": [
{
"type": "header",
"text": {
"type": "plain_text",
"text": "'"${STATUS_TEXT}"'",
"emoji": true
}
},
{
"type": "section",
"fields": [
{
"type": "mrkdwn",
"text": "'"${PR_TITLE}"'"
}
]
},
{
"type": "actions",
"elements": [
{
"type": "button",
"text": {
"type": "plain_text",
"text": "View PR"
},
"url": "'"${PR_URL}"'"
}
]
}
]
}' "${{ secrets.DEPENDABOT_PRS_SLACK_WEBHOOK }}"
env:
SLACK_WEBHOOK: ${{ secrets.DEPENDABOT_PRS_SLACK_WEBHOOK }}
PACKAGE_ECOSYSTEM: ${{ steps.metadata.outputs.package-ecosystem }}
PR_NUMBER: ${{ github.event.pull_request.number }}
PR_TITLE: ${{ github.event.pull_request.title }}
PR_URL: ${{ github.event.pull_request.html_url }}
-172
View File
@@ -1,172 +0,0 @@
name: deploy
on:
# Via workflow_call, called from ci.yaml
workflow_call:
inputs:
image:
description: "Image and tag to potentially deploy. Current branch will be validated against should-deploy check."
required: true
type: string
secrets:
FLY_API_TOKEN:
required: true
FLY_PARIS_CODER_PROXY_SESSION_TOKEN:
required: true
FLY_SYDNEY_CODER_PROXY_SESSION_TOKEN:
required: true
FLY_SAO_PAULO_CODER_PROXY_SESSION_TOKEN:
required: true
FLY_JNB_CODER_PROXY_SESSION_TOKEN:
required: true
permissions:
contents: read
concurrency:
group: ${{ github.workflow }} # no per-branch concurrency
cancel-in-progress: false
jobs:
# Determines if the given branch should be deployed to dogfood.
should-deploy:
name: should-deploy
runs-on: ubuntu-latest
outputs:
verdict: ${{ steps.check.outputs.verdict }} # DEPLOY or NOOP
steps:
- name: Harden Runner
uses: step-security/harden-runner@95d9a5deda9de15063e7595e9719c11c38c90ae2 # v2.13.2
with:
egress-policy: audit
- name: Checkout
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
with:
fetch-depth: 0
persist-credentials: false
- name: Check if deploy is enabled
id: check
run: |
set -euo pipefail
verdict="$(./scripts/should_deploy.sh)"
echo "verdict=$verdict" >> "$GITHUB_OUTPUT"
deploy:
name: "deploy"
runs-on: ubuntu-latest
timeout-minutes: 30
needs: should-deploy
if: needs.should-deploy.outputs.verdict == 'DEPLOY'
permissions:
contents: read
id-token: write
packages: write # to retag image as dogfood
steps:
- name: Harden Runner
uses: step-security/harden-runner@95d9a5deda9de15063e7595e9719c11c38c90ae2 # v2.13.2
with:
egress-policy: audit
- name: Checkout
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
with:
fetch-depth: 0
persist-credentials: false
- name: GHCR Login
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Authenticate to Google Cloud
uses: google-github-actions/auth@7c6bc770dae815cd3e89ee6cdf493a5fab2cc093 # v3.0.0
with:
workload_identity_provider: ${{ vars.GCP_WORKLOAD_ID_PROVIDER }}
service_account: ${{ vars.GCP_SERVICE_ACCOUNT }}
- name: Set up Google Cloud SDK
uses: google-github-actions/setup-gcloud@aa5489c8933f4cc7a4f7d45035b3b1440c9c10db # v3.0.1
- name: Set up Flux CLI
uses: fluxcd/flux2/action@8454b02a32e48d775b9f563cb51fdcb1787b5b93 # v2.7.5
with:
# Keep this and the github action up to date with the version of flux installed in dogfood cluster
version: "2.7.0"
- name: Get Cluster Credentials
uses: google-github-actions/get-gke-credentials@3da1e46a907576cefaa90c484278bb5b259dd395 # v3.0.0
with:
cluster_name: dogfood-v2
location: us-central1-a
project_id: coder-dogfood-v2
# Retag image as dogfood while maintaining the multi-arch manifest
- name: Tag image as dogfood
run: docker buildx imagetools create --tag "ghcr.io/coder/coder-preview:dogfood" "$IMAGE"
env:
IMAGE: ${{ inputs.image }}
- name: Reconcile Flux
run: |
set -euxo pipefail
flux --namespace flux-system reconcile source git flux-system
flux --namespace flux-system reconcile source git coder-main
flux --namespace flux-system reconcile kustomization flux-system
flux --namespace flux-system reconcile kustomization coder
flux --namespace flux-system reconcile source chart coder-coder
flux --namespace flux-system reconcile source chart coder-coder-provisioner
flux --namespace coder reconcile helmrelease coder
flux --namespace coder reconcile helmrelease coder-provisioner
flux --namespace coder reconcile helmrelease coder-provisioner-tagged
flux --namespace coder reconcile helmrelease coder-provisioner-tagged-prebuilds
# Just updating Flux is usually not enough. The Helm release may get
# redeployed, but unless something causes the Deployment to update the
# pods won't be recreated. It's important that the pods get recreated,
# since we use `imagePullPolicy: Always` to ensure we're running the
# latest image.
- name: Rollout Deployment
run: |
set -euxo pipefail
kubectl --namespace coder rollout restart deployment/coder
kubectl --namespace coder rollout status deployment/coder
kubectl --namespace coder rollout restart deployment/coder-provisioner
kubectl --namespace coder rollout status deployment/coder-provisioner
kubectl --namespace coder rollout restart deployment/coder-provisioner-tagged
kubectl --namespace coder rollout status deployment/coder-provisioner-tagged
kubectl --namespace coder rollout restart deployment/coder-provisioner-tagged-prebuilds
kubectl --namespace coder rollout status deployment/coder-provisioner-tagged-prebuilds
deploy-wsproxies:
runs-on: ubuntu-latest
needs: deploy
steps:
- name: Harden Runner
uses: step-security/harden-runner@95d9a5deda9de15063e7595e9719c11c38c90ae2 # v2.13.2
with:
egress-policy: audit
- name: Checkout
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
with:
fetch-depth: 0
persist-credentials: false
- name: Setup flyctl
uses: superfly/flyctl-actions/setup-flyctl@fc53c09e1bc3be6f54706524e3b82c4f462f77be # v1.5
- name: Deploy workspace proxies
run: |
flyctl deploy --image "$IMAGE" --app paris-coder --config ./.github/fly-wsproxies/paris-coder.toml --env "CODER_PROXY_SESSION_TOKEN=$TOKEN_PARIS" --yes
flyctl deploy --image "$IMAGE" --app sydney-coder --config ./.github/fly-wsproxies/sydney-coder.toml --env "CODER_PROXY_SESSION_TOKEN=$TOKEN_SYDNEY" --yes
flyctl deploy --image "$IMAGE" --app jnb-coder --config ./.github/fly-wsproxies/jnb-coder.toml --env "CODER_PROXY_SESSION_TOKEN=$TOKEN_JNB" --yes
env:
FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}
IMAGE: ${{ inputs.image }}
TOKEN_PARIS: ${{ secrets.FLY_PARIS_CODER_PROXY_SESSION_TOKEN }}
TOKEN_SYDNEY: ${{ secrets.FLY_SYDNEY_CODER_PROXY_SESSION_TOKEN }}
TOKEN_JNB: ${{ secrets.FLY_JNB_CODER_PROXY_SESSION_TOKEN }}
-205
View File
@@ -1,205 +0,0 @@
# This workflow checks if a PR requires documentation updates.
# It creates a Coder Task that uses AI to analyze the PR changes,
# search existing docs, and comment with recommendations.
#
# Triggered by: Adding the "doc-check" label to a PR, or manual dispatch.
name: AI Documentation Check
on:
pull_request:
types:
- labeled
workflow_dispatch:
inputs:
pr_url:
description: "Pull Request URL to check"
required: true
type: string
template_preset:
description: "Template preset to use"
required: false
default: ""
type: string
jobs:
doc-check:
name: Analyze PR for Documentation Updates Needed
runs-on: ubuntu-latest
if: |
(github.event.label.name == 'doc-check' || github.event_name == 'workflow_dispatch') &&
(github.event.pull_request.draft == false || github.event_name == 'workflow_dispatch')
timeout-minutes: 30
env:
CODER_URL: ${{ secrets.DOC_CHECK_CODER_URL }}
CODER_SESSION_TOKEN: ${{ secrets.DOC_CHECK_CODER_SESSION_TOKEN }}
permissions:
contents: read
pull-requests: write
actions: write
steps:
- name: Determine PR Context
id: determine-context
env:
GITHUB_ACTOR: ${{ github.actor }}
GITHUB_EVENT_NAME: ${{ github.event_name }}
GITHUB_EVENT_PR_HTML_URL: ${{ github.event.pull_request.html_url }}
GITHUB_EVENT_PR_NUMBER: ${{ github.event.pull_request.number }}
GITHUB_EVENT_SENDER_ID: ${{ github.event.sender.id }}
GITHUB_EVENT_SENDER_LOGIN: ${{ github.event.sender.login }}
INPUTS_PR_URL: ${{ inputs.pr_url }}
INPUTS_TEMPLATE_PRESET: ${{ inputs.template_preset || '' }}
GH_TOKEN: ${{ github.token }}
run: |
echo "Using template preset: ${INPUTS_TEMPLATE_PRESET}"
echo "template_preset=${INPUTS_TEMPLATE_PRESET}" >> "${GITHUB_OUTPUT}"
# For workflow_dispatch, use the provided PR URL
if [[ "${GITHUB_EVENT_NAME}" == "workflow_dispatch" ]]; then
if ! GITHUB_USER_ID=$(gh api "users/${GITHUB_ACTOR}" --jq '.id'); then
echo "::error::Failed to get GitHub user ID for actor ${GITHUB_ACTOR}"
exit 1
fi
echo "Using workflow_dispatch actor: ${GITHUB_ACTOR} (ID: ${GITHUB_USER_ID})"
echo "github_user_id=${GITHUB_USER_ID}" >> "${GITHUB_OUTPUT}"
echo "github_username=${GITHUB_ACTOR}" >> "${GITHUB_OUTPUT}"
echo "Using PR URL: ${INPUTS_PR_URL}"
# Convert /pull/ to /issues/ for create-task-action compatibility
ISSUE_URL="${INPUTS_PR_URL/\/pull\//\/issues\/}"
echo "pr_url=${ISSUE_URL}" >> "${GITHUB_OUTPUT}"
# Extract PR number from URL for later use
PR_NUMBER=$(echo "${INPUTS_PR_URL}" | grep -oP '(?<=pull/)\d+')
echo "pr_number=${PR_NUMBER}" >> "${GITHUB_OUTPUT}"
elif [[ "${GITHUB_EVENT_NAME}" == "pull_request" ]]; then
GITHUB_USER_ID=${GITHUB_EVENT_SENDER_ID}
echo "Using label adder: ${GITHUB_EVENT_SENDER_LOGIN} (ID: ${GITHUB_USER_ID})"
echo "github_user_id=${GITHUB_USER_ID}" >> "${GITHUB_OUTPUT}"
echo "github_username=${GITHUB_EVENT_SENDER_LOGIN}" >> "${GITHUB_OUTPUT}"
echo "Using PR URL: ${GITHUB_EVENT_PR_HTML_URL}"
# Convert /pull/ to /issues/ for create-task-action compatibility
ISSUE_URL="${GITHUB_EVENT_PR_HTML_URL/\/pull\//\/issues\/}"
echo "pr_url=${ISSUE_URL}" >> "${GITHUB_OUTPUT}"
echo "pr_number=${GITHUB_EVENT_PR_NUMBER}" >> "${GITHUB_OUTPUT}"
else
echo "::error::Unsupported event type: ${GITHUB_EVENT_NAME}"
exit 1
fi
- name: Extract changed files and build prompt
id: extract-context
env:
PR_URL: ${{ steps.determine-context.outputs.pr_url }}
PR_NUMBER: ${{ steps.determine-context.outputs.pr_number }}
GH_TOKEN: ${{ github.token }}
run: |
echo "Analyzing PR #${PR_NUMBER}"
# Build task prompt - using unquoted heredoc so variables expand
TASK_PROMPT=$(cat <<EOF
Review PR #${PR_NUMBER} and determine if documentation needs updating or creating.
PR URL: ${PR_URL}
WORKFLOW:
1. Setup (repo is pre-cloned at ~/coder)
cd ~/coder
git fetch origin pull/${PR_NUMBER}/head:pr-${PR_NUMBER}
git checkout pr-${PR_NUMBER}
2. Get PR info
Use GitHub MCP tools to get PR title, body, and diff
Or use: git diff main...pr-${PR_NUMBER}
3. Understand Changes
Read the diff and identify what changed
Ask: Is this user-facing? Does it change behavior? Is it a new feature?
4. Search for Related Docs
cat ~/coder/docs/manifest.json | jq '.routes[] | {title, path}' | head -50
grep -ri "relevant_term" ~/coder/docs/ --include="*.md"
5. Decide
NEEDS DOCS if: New feature, API change, CLI change, behavior change, user-visible
NO DOCS if: Internal refactor, test-only, already documented, non-user-facing, dependency updates
FIRST check: Did this PR already update docs? If yes and complete, say "No Changes Needed"
6. Comment on the PR using this format
COMMENT FORMAT:
## 📚 Documentation Check
### ✅ Updates Needed
- **[docs/path/file.md](github_link)** - Brief what needs changing
### 📝 New Docs Needed
- **docs/suggested/location.md** - What should be documented
### ✨ No Changes Needed
[Reason: Documents already updated in PR | Internal changes only | Test-only | No user-facing impact]
---
*This comment was generated by an AI Agent through [Coder Tasks](https://coder.com/docs/ai-coder/tasks)*
DOCS STRUCTURE:
Read ~/coder/docs/manifest.json for the complete documentation structure.
Common areas include: reference/, admin/, user-guides/, ai-coder/, install/, tutorials/
But check manifest.json - it has everything.
EOF
)
# Output the prompt
{
echo "task_prompt<<EOFOUTPUT"
echo "${TASK_PROMPT}"
echo "EOFOUTPUT"
} >> "${GITHUB_OUTPUT}"
- name: Checkout create-task-action
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
with:
fetch-depth: 1
path: ./.github/actions/create-task-action
persist-credentials: false
ref: main
repository: coder/create-task-action
- name: Create Coder Task for Documentation Check
id: create_task
uses: ./.github/actions/create-task-action
with:
coder-url: ${{ secrets.DOC_CHECK_CODER_URL }}
coder-token: ${{ secrets.DOC_CHECK_CODER_SESSION_TOKEN }}
coder-organization: "default"
coder-template-name: coder
coder-template-preset: ${{ steps.determine-context.outputs.template_preset }}
coder-task-name-prefix: doc-check
coder-task-prompt: ${{ steps.extract-context.outputs.task_prompt }}
github-user-id: ${{ steps.determine-context.outputs.github_user_id }}
github-token: ${{ github.token }}
github-issue-url: ${{ steps.determine-context.outputs.pr_url }}
comment-on-issue: true
- name: Write outputs
env:
TASK_CREATED: ${{ steps.create_task.outputs.task-created }}
TASK_NAME: ${{ steps.create_task.outputs.task-name }}
TASK_URL: ${{ steps.create_task.outputs.task-url }}
PR_URL: ${{ steps.determine-context.outputs.pr_url }}
run: |
{
echo "## Documentation Check Task"
echo ""
echo "**PR:** ${PR_URL}"
echo "**Task created:** ${TASK_CREATED}"
echo "**Task name:** ${TASK_NAME}"
echo "**Task URL:** ${TASK_URL}"
echo ""
echo "The Coder task is analyzing the PR changes and will comment with documentation recommendations."
} >> "${GITHUB_STEP_SUMMARY}"
-106
View File
@@ -1,106 +0,0 @@
name: docker-base
on:
push:
branches:
- main
paths:
- scripts/Dockerfile.base
- scripts/Dockerfile
pull_request:
paths:
- scripts/Dockerfile.base
- .github/workflows/docker-base.yaml
schedule:
# Run every week at 09:43 on Monday, Wednesday and Friday. We build this
# frequently to ensure that packages are up-to-date.
- cron: "43 9 * * 1,3,5"
workflow_dispatch:
permissions:
contents: read
# Avoid running multiple jobs for the same commit.
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}-docker-base
jobs:
build:
permissions:
# Necessary for depot.dev authentication.
id-token: write
# Necessary to push docker images to ghcr.io.
packages: write
runs-on: ubuntu-latest
if: github.repository_owner == 'coder'
steps:
- name: Harden Runner
uses: step-security/harden-runner@95d9a5deda9de15063e7595e9719c11c38c90ae2 # v2.13.2
with:
egress-policy: audit
- name: Checkout
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
with:
persist-credentials: false
- name: Docker login
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Create empty base-build-context directory
run: mkdir base-build-context
- name: Install depot.dev CLI
uses: depot/setup-action@b0b1ea4f69e92ebf5dea3f8713a1b0c37b2126a5 # v1.6.0
# This uses OIDC authentication, so no auth variables are required.
- name: Build base Docker image via depot.dev
uses: depot/build-push-action@9785b135c3c76c33db102e45be96a25ab55cd507 # v1.16.2
with:
project: wl5hnrrkns
context: base-build-context
file: scripts/Dockerfile.base
platforms: linux/amd64,linux/arm64,linux/arm/v7
provenance: true
pull: true
no-cache: true
push: ${{ github.event_name != 'pull_request' }}
tags: |
ghcr.io/coder/coder-base:latest
- name: Verify that images are pushed properly
if: github.event_name != 'pull_request'
run: |
# retry 10 times with a 5 second delay as the images may not be
# available immediately
for i in {1..10}; do
rc=0
raw_manifests=$(docker buildx imagetools inspect --raw ghcr.io/coder/coder-base:latest) || rc=$?
if [[ "$rc" -eq 0 ]]; then
break
fi
if [[ "$i" -eq 10 ]]; then
echo "Failed to pull manifests after 10 retries"
exit 1
fi
echo "Failed to pull manifests, retrying in 5 seconds"
sleep 5
done
manifests=$(
echo "$raw_manifests" | \
jq -r '.manifests[].platform | .os + "/" + .architecture + (if .variant then "/" + .variant else "" end)'
)
# Verify all 3 platforms are present.
set -euxo pipefail
echo "$manifests" | grep -q linux/amd64
echo "$manifests" | grep -q linux/arm64
echo "$manifests" | grep -q linux/arm/v7
-56
View File
@@ -1,56 +0,0 @@
name: Docs CI
on:
push:
branches:
- main
paths:
- "docs/**"
- "**.md"
- ".github/workflows/docs-ci.yaml"
pull_request:
paths:
- "docs/**"
- "**.md"
- ".github/workflows/docs-ci.yaml"
permissions:
contents: read
jobs:
docs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
with:
persist-credentials: false
- name: Setup Node
uses: ./.github/actions/setup-node
- uses: tj-actions/changed-files@abdd2f68ea150cee8f236d4a9fb4e0f2491abf1b # v45.0.7
id: changed-files
with:
files: |
docs/**
**.md
separator: ","
- name: lint
if: steps.changed-files.outputs.any_changed == 'true'
run: |
# shellcheck disable=SC2086
pnpm exec markdownlint-cli2 $ALL_CHANGED_FILES
env:
ALL_CHANGED_FILES: ${{ steps.changed-files.outputs.all_changed_files }}
- name: fmt
if: steps.changed-files.outputs.any_changed == 'true'
run: |
# markdown-table-formatter requires a space separated list of files
# shellcheck disable=SC2086
echo $ALL_CHANGED_FILES | tr ',' '\n' | pnpm exec markdown-table-formatter --check
env:
ALL_CHANGED_FILES: ${{ steps.changed-files.outputs.all_changed_files }}
-186
View File
@@ -1,186 +0,0 @@
name: dogfood
on:
push:
branches:
- main
paths:
- "dogfood/**"
- ".github/workflows/dogfood.yaml"
- "flake.lock"
- "flake.nix"
pull_request:
paths:
- "dogfood/**"
- ".github/workflows/dogfood.yaml"
- "flake.lock"
- "flake.nix"
workflow_dispatch:
permissions:
contents: read
jobs:
build_image:
if: github.actor != 'dependabot[bot]' # Skip Dependabot PRs
runs-on: ${{ github.repository_owner == 'coder' && 'depot-ubuntu-22.04-4' || 'ubuntu-latest' }}
steps:
- name: Harden Runner
uses: step-security/harden-runner@95d9a5deda9de15063e7595e9719c11c38c90ae2 # v2.13.2
with:
egress-policy: audit
- name: Checkout
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
with:
persist-credentials: false
- name: Setup Nix
uses: nixbuild/nix-quick-install-action@2c9db80fb984ceb1bcaa77cdda3fdf8cfba92035 # v34
with:
# Pinning to 2.28 here, as Nix gets a "error: [json.exception.type_error.302] type must be array, but is string"
# on version 2.29 and above.
nix_version: "2.28.5"
- uses: nix-community/cache-nix-action@135667ec418502fa5a3598af6fb9eb733888ce6a # v6.1.3
with:
# restore and save a cache using this key
primary-key: nix-${{ runner.os }}-${{ hashFiles('**/*.nix', '**/flake.lock') }}
# if there's no cache hit, restore a cache by this prefix
restore-prefixes-first-match: nix-${{ runner.os }}-
# collect garbage until Nix store size (in bytes) is at most this number
# before trying to save a new cache
# 1G = 1073741824
gc-max-store-size-linux: 5G
# do purge caches
purge: true
# purge all versions of the cache
purge-prefixes: nix-${{ runner.os }}-
# created more than this number of seconds ago relative to the start of the `Post Restore` phase
purge-created: 0
# except the version with the `primary-key`, if it exists
purge-primary-key: never
- name: Get branch name
id: branch-name
uses: tj-actions/branch-names@5250492686b253f06fa55861556d1027b067aeb5 # v9.0.2
- name: "Branch name to Docker tag name"
id: docker-tag-name
run: |
# Replace / with --, e.g. user/feature => user--feature.
tag=${BRANCH_NAME//\//--}
echo "tag=${tag}" >> "$GITHUB_OUTPUT"
env:
BRANCH_NAME: ${{ steps.branch-name.outputs.current_branch }}
- name: Set up Depot CLI
uses: depot/setup-action@b0b1ea4f69e92ebf5dea3f8713a1b0c37b2126a5 # v1.6.0
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
- name: Login to DockerHub
if: github.ref == 'refs/heads/main'
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_PASSWORD }}
- name: Build and push Non-Nix image
uses: depot/build-push-action@9785b135c3c76c33db102e45be96a25ab55cd507 # v1.16.2
with:
project: b4q6ltmpzh
token: ${{ secrets.DEPOT_TOKEN }}
buildx-fallback: true
context: "{{defaultContext}}:dogfood/coder"
pull: true
save: true
push: ${{ github.ref == 'refs/heads/main' }}
tags: "codercom/oss-dogfood:${{ steps.docker-tag-name.outputs.tag }},codercom/oss-dogfood:latest"
- name: Build Nix image
run: nix build .#dev_image
- name: Push Nix image
if: github.ref == 'refs/heads/main'
run: |
docker load -i result
CURRENT_SYSTEM=$(nix eval --impure --raw --expr 'builtins.currentSystem')
docker image tag "codercom/oss-dogfood-nix:latest-$CURRENT_SYSTEM" "codercom/oss-dogfood-nix:${DOCKER_TAG}"
docker image push "codercom/oss-dogfood-nix:${DOCKER_TAG}"
docker image tag "codercom/oss-dogfood-nix:latest-$CURRENT_SYSTEM" "codercom/oss-dogfood-nix:latest"
docker image push "codercom/oss-dogfood-nix:latest"
env:
DOCKER_TAG: ${{ steps.docker-tag-name.outputs.tag }}
deploy_template:
needs: build_image
runs-on: ubuntu-latest
permissions:
# Necessary for GCP authentication (https://github.com/google-github-actions/setup-gcloud#usage)
id-token: write
steps:
- name: Harden Runner
uses: step-security/harden-runner@95d9a5deda9de15063e7595e9719c11c38c90ae2 # v2.13.2
with:
egress-policy: audit
- name: Checkout
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
with:
persist-credentials: false
- name: Setup Terraform
uses: ./.github/actions/setup-tf
- name: Authenticate to Google Cloud
uses: google-github-actions/auth@7c6bc770dae815cd3e89ee6cdf493a5fab2cc093 # v3.0.0
with:
workload_identity_provider: ${{ vars.GCP_WORKLOAD_ID_PROVIDER }}
service_account: ${{ vars.GCP_SERVICE_ACCOUNT }}
- name: Terraform init and validate
run: |
pushd dogfood/
terraform init
terraform validate
popd
pushd dogfood/coder
terraform init
terraform validate
popd
pushd dogfood/coder-envbuilder
terraform init
terraform validate
popd
- name: Get short commit SHA
if: github.ref == 'refs/heads/main'
id: vars
run: echo "sha_short=$(git rev-parse --short HEAD)" >> "$GITHUB_OUTPUT"
- name: Get latest commit title
if: github.ref == 'refs/heads/main'
id: message
run: echo "pr_title=$(git log --format=%s -n 1 ${{ github.sha }})" >> "$GITHUB_OUTPUT"
- name: "Push template"
if: github.ref == 'refs/heads/main'
run: |
cd dogfood
terraform apply -auto-approve
env:
# Consumed by coderd provider
CODER_URL: https://dev.coder.com
CODER_SESSION_TOKEN: ${{ secrets.CODER_SESSION_TOKEN }}
# Template source & details
TF_VAR_CODER_DOGFOOD_ANTHROPIC_API_KEY: ${{ secrets.CODER_DOGFOOD_ANTHROPIC_API_KEY }}
TF_VAR_CODER_TEMPLATE_NAME: ${{ secrets.CODER_TEMPLATE_NAME }}
TF_VAR_CODER_TEMPLATE_VERSION: ${{ steps.vars.outputs.sha_short }}
TF_VAR_CODER_TEMPLATE_DIR: ./coder
TF_VAR_CODER_TEMPLATE_MESSAGE: ${{ steps.message.outputs.pr_title }}
TF_LOG: info
-204
View File
@@ -1,204 +0,0 @@
# The nightly-gauntlet runs tests that are either too flaky or too slow to block
# every PR.
name: nightly-gauntlet
on:
schedule:
# Every day at 4AM
- cron: "0 4 * * 1-5"
workflow_dispatch:
permissions:
contents: read
jobs:
test-go-pg:
# make sure to adjust NUM_PARALLEL_PACKAGES and NUM_PARALLEL_TESTS below
# when changing runner sizes
runs-on: ${{ matrix.os == 'macos-latest' && github.repository_owner == 'coder' && 'depot-macos-latest' || matrix.os == 'windows-2022' && github.repository_owner == 'coder' && 'depot-windows-2022-16' || matrix.os }}
# This timeout must be greater than the timeout set by `go test` in
# `make test-postgres` to ensure we receive a trace of running
# goroutines. Setting this to the timeout +5m should work quite well
# even if some of the preceding steps are slow.
timeout-minutes: 25
strategy:
matrix:
os:
- macos-latest
- windows-2022
steps:
- name: Harden Runner
uses: step-security/harden-runner@95d9a5deda9de15063e7595e9719c11c38c90ae2 # v2.13.2
with:
egress-policy: audit
# macOS indexes all new files in the background. Our Postgres tests
# create and destroy thousands of databases on disk, and Spotlight
# tries to index all of them, seriously slowing down the tests.
- name: Disable Spotlight Indexing
if: runner.os == 'macOS'
run: |
enabled=$(sudo mdutil -a -s | { grep -Fc "Indexing enabled" || true; })
if [ "$enabled" -eq 0 ]; then
echo "Spotlight indexing is already disabled"
exit 0
fi
sudo mdutil -a -i off
sudo mdutil -X /
sudo launchctl bootout system /System/Library/LaunchDaemons/com.apple.metadata.mds.plist
# Set up RAM disks to speed up the rest of the job. This action is in
# a separate repository to allow its use before actions/checkout.
- name: Setup RAM Disks
if: runner.os == 'Windows'
uses: coder/setup-ramdisk-action@e1100847ab2d7bcd9d14bcda8f2d1b0f07b36f1b # v0.1.0
- name: Checkout
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
with:
fetch-depth: 1
persist-credentials: false
- name: Setup Go
uses: ./.github/actions/setup-go
with:
# Runners have Go baked-in and Go will automatically
# download the toolchain configured in go.mod, so we don't
# need to reinstall it. It's faster on Windows runners.
use-preinstalled-go: ${{ runner.os == 'Windows' }}
- name: Setup Terraform
uses: ./.github/actions/setup-tf
- name: Setup Embedded Postgres Cache Paths
id: embedded-pg-cache
uses: ./.github/actions/setup-embedded-pg-cache-paths
- name: Download Embedded Postgres Cache
id: download-embedded-pg-cache
uses: ./.github/actions/embedded-pg-cache/download
with:
key-prefix: embedded-pg-${{ runner.os }}-${{ runner.arch }}
cache-path: ${{ steps.embedded-pg-cache.outputs.cached-dirs }}
- name: Test with PostgreSQL Database
env:
POSTGRES_VERSION: "13"
TS_DEBUG_DISCO: "true"
LC_CTYPE: "en_US.UTF-8"
LC_ALL: "en_US.UTF-8"
shell: bash
run: |
set -o errexit
set -o pipefail
if [ "${{ runner.os }}" == "Windows" ]; then
# Create a temp dir on the R: ramdisk drive for Windows. The default
# C: drive is extremely slow: https://github.com/actions/runner-images/issues/8755
mkdir -p "R:/temp/embedded-pg"
go run scripts/embedded-pg/main.go -path "R:/temp/embedded-pg" -cache "${EMBEDDED_PG_CACHE_DIR}"
elif [ "${{ runner.os }}" == "macOS" ]; then
# Postgres runs faster on a ramdisk on macOS too
mkdir -p /tmp/tmpfs
sudo mount_tmpfs -o noowners -s 8g /tmp/tmpfs
go run scripts/embedded-pg/main.go -path /tmp/tmpfs/embedded-pg -cache "${EMBEDDED_PG_CACHE_DIR}"
elif [ "${{ runner.os }}" == "Linux" ]; then
make test-postgres-docker
fi
# if macOS, install google-chrome for scaletests
# As another concern, should we really have this kind of external dependency
# requirement on standard CI?
if [ "${{ matrix.os }}" == "macos-latest" ]; then
brew install google-chrome
fi
# macOS will output "The default interactive shell is now zsh"
# intermittently in CI...
if [ "${{ matrix.os }}" == "macos-latest" ]; then
touch ~/.bash_profile && echo "export BASH_SILENCE_DEPRECATION_WARNING=1" >> ~/.bash_profile
fi
if [ "${{ runner.os }}" == "Windows" ]; then
# Our Windows runners have 16 cores.
# On Windows Postgres chokes up when we have 16x16=256 tests
# running in parallel, and dbtestutil.NewDB starts to take more than
# 10s to complete sometimes causing test timeouts. With 16x8=128 tests
# Postgres tends not to choke.
NUM_PARALLEL_PACKAGES=8
NUM_PARALLEL_TESTS=16
elif [ "${{ runner.os }}" == "macOS" ]; then
# Our macOS runners have 8 cores. We set NUM_PARALLEL_TESTS to 16
# because the tests complete faster and Postgres doesn't choke. It seems
# that macOS's tmpfs is faster than the one on Windows.
NUM_PARALLEL_PACKAGES=8
NUM_PARALLEL_TESTS=16
elif [ "${{ runner.os }}" == "Linux" ]; then
# Our Linux runners have 8 cores.
NUM_PARALLEL_PACKAGES=8
NUM_PARALLEL_TESTS=8
fi
# run tests without cache
TESTCOUNT="-count=1"
DB=ci gotestsum \
--format standard-quiet --packages "./..." \
-- -timeout=20m -v -p "$NUM_PARALLEL_PACKAGES" -parallel="$NUM_PARALLEL_TESTS" "$TESTCOUNT"
- name: Upload Embedded Postgres Cache
uses: ./.github/actions/embedded-pg-cache/upload
# We only use the embedded Postgres cache on macOS and Windows runners.
if: runner.OS == 'macOS' || runner.OS == 'Windows'
with:
cache-key: ${{ steps.download-embedded-pg-cache.outputs.cache-key }}
cache-path: "${{ steps.embedded-pg-cache.outputs.embedded-pg-cache }}"
- name: Upload test stats to Datadog
timeout-minutes: 1
continue-on-error: true
uses: ./.github/actions/upload-datadog
if: success() || failure()
with:
api-key: ${{ secrets.DATADOG_API_KEY }}
notify-slack-on-failure:
needs:
- test-go-pg
runs-on: ubuntu-latest
if: failure() && github.ref == 'refs/heads/main'
steps:
- name: Send Slack notification
run: |
ESCAPED_PROMPT=$(printf "%s" "<@U09LQ75AHKR> $BLINK_CI_FAILURE_PROMPT" | jq -Rsa .)
curl -X POST -H 'Content-type: application/json' \
--data '{
"blocks": [
{
"type": "header",
"text": {
"type": "plain_text",
"text": "❌ Nightly gauntlet failed",
"emoji": true
}
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "*View failure:* <'"${RUN_URL}"'|Click here>"
}
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": '"$ESCAPED_PROMPT"'
}
}
]
}' "${SLACK_WEBHOOK}"
env:
SLACK_WEBHOOK: ${{ secrets.CI_FAILURE_SLACK_WEBHOOK }}
RUN_URL: "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
BLINK_CI_FAILURE_PROMPT: ${{ vars.BLINK_CI_FAILURE_PROMPT }}
-23
View File
@@ -1,23 +0,0 @@
# Filtering pull requests is much easier when we can reliably guarantee
# that the "Assignee" field is populated.
name: PR Auto Assign
on:
# zizmor: ignore[dangerous-triggers] We explicitly want to run on pull_request_target.
pull_request_target:
types: [opened]
permissions:
pull-requests: write
jobs:
assign-author:
runs-on: ubuntu-latest
steps:
- name: Harden Runner
uses: step-security/harden-runner@95d9a5deda9de15063e7595e9719c11c38c90ae2 # v2.13.2
with:
egress-policy: audit
- name: Assign author
uses: toshimaru/auto-author-assign@16f0022cf3d7970c106d8d1105f75a1165edb516 # v2.1.1
-91
View File
@@ -1,91 +0,0 @@
name: pr-cleanup
on:
pull_request:
types: closed
workflow_dispatch:
inputs:
pr_number:
description: "PR number"
required: true
permissions:
contents: read
jobs:
cleanup:
runs-on: "ubuntu-latest"
permissions:
# Necessary to delete docker images from ghcr.io.
packages: write
steps:
- name: Harden Runner
uses: step-security/harden-runner@95d9a5deda9de15063e7595e9719c11c38c90ae2 # v2.13.2
with:
egress-policy: audit
- name: Get PR number
id: pr_number
run: |
if [ -n "${{ github.event.pull_request.number }}" ]; then
echo "PR_NUMBER=${{ github.event.pull_request.number }}" >> "$GITHUB_OUTPUT"
else
echo "PR_NUMBER=${PR_NUMBER}" >> "$GITHUB_OUTPUT"
fi
env:
PR_NUMBER: ${{ github.event.inputs.pr_number }}
- name: Delete image
continue-on-error: true
uses: bots-house/ghcr-delete-image-action@3827559c68cb4dcdf54d813ea9853be6d468d3a4 # v1.1.0
with:
owner: coder
name: coder-preview
token: ${{ secrets.GITHUB_TOKEN }}
tag: pr${{ steps.pr_number.outputs.PR_NUMBER }}
- name: Set up kubeconfig
run: |
set -euo pipefail
mkdir -p ~/.kube
echo "${{ secrets.PR_DEPLOYMENTS_KUBECONFIG }}" > ~/.kube/config
export KUBECONFIG=~/.kube/config
- name: Delete helm release
run: |
set -euo pipefail
helm delete --namespace "pr${PR_NUMBER}" "pr${PR_NUMBER}" || echo "helm release not found"
env:
PR_NUMBER: ${{ steps.pr_number.outputs.PR_NUMBER }}
- name: "Remove PR namespace"
run: |
kubectl delete namespace "pr${PR_NUMBER}" || echo "namespace not found"
env:
PR_NUMBER: ${{ steps.pr_number.outputs.PR_NUMBER }}
- name: "Remove DNS records"
run: |
set -euo pipefail
# Get identifier for the record
record_id=$(curl -X GET "https://api.cloudflare.com/client/v4/zones/${{ secrets.PR_DEPLOYMENTS_ZONE_ID }}/dns_records?name=%2A.pr${PR_NUMBER}.${{ secrets.PR_DEPLOYMENTS_DOMAIN }}" \
-H "Authorization: Bearer ${{ secrets.PR_DEPLOYMENTS_CLOUDFLARE_API_TOKEN }}" \
-H "Content-Type:application/json" | jq -r '.result[0].id') || echo "DNS record not found"
echo "::add-mask::$record_id"
# Delete the record
(
curl -X DELETE "https://api.cloudflare.com/client/v4/zones/${{ secrets.PR_DEPLOYMENTS_ZONE_ID }}/dns_records/$record_id" \
-H "Authorization: Bearer ${{ secrets.PR_DEPLOYMENTS_CLOUDFLARE_API_TOKEN }}" \
-H "Content-Type:application/json" | jq -r '.success'
) || echo "DNS record not found"
env:
PR_NUMBER: ${{ steps.pr_number.outputs.PR_NUMBER }}
- name: "Delete certificate"
if: ${{ github.event.pull_request.merged == true }}
run: |
set -euxo pipefail
kubectl delete certificate "pr${PR_NUMBER}-tls" -n pr-deployment-certs || echo "certificate not found"
env:
PR_NUMBER: ${{ steps.pr_number.outputs.PR_NUMBER }}
-528
View File
@@ -1,528 +0,0 @@
# This action will trigger when
# 1. when the workflow is manually triggered
# 2. ./scripts/deploy_pr.sh is run locally
# 3. when a PR is updated
name: Deploy PR
on:
push:
branches-ignore:
- main
- "temp-cherry-pick-*"
workflow_dispatch:
inputs:
experiments:
description: "Experiments to enable"
required: false
type: string
default: "*"
build:
description: "Force new build"
required: false
type: boolean
default: false
deploy:
description: "Force new deployment"
required: false
type: boolean
default: false
env:
REPO: ghcr.io/coder/coder-preview
permissions:
contents: read
jobs:
check_pr:
runs-on: ubuntu-latest
outputs:
PR_OPEN: ${{ steps.check_pr.outputs.pr_open }}
steps:
- name: Harden Runner
uses: step-security/harden-runner@95d9a5deda9de15063e7595e9719c11c38c90ae2 # v2.13.2
with:
egress-policy: audit
- name: Checkout
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
with:
persist-credentials: false
- name: Check if PR is open
id: check_pr
run: |
set -euo pipefail
pr_open=true
if [[ "$(gh pr view --json state | jq -r '.state')" != "OPEN" ]]; then
echo "PR doesn't exist or is closed."
pr_open=false
fi
echo "pr_open=$pr_open" >> "$GITHUB_OUTPUT"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
get_info:
needs: check_pr
if: ${{ needs.check_pr.outputs.PR_OPEN == 'true' }}
outputs:
PR_NUMBER: ${{ steps.pr_info.outputs.PR_NUMBER }}
PR_TITLE: ${{ steps.pr_info.outputs.PR_TITLE }}
PR_URL: ${{ steps.pr_info.outputs.PR_URL }}
CODER_BASE_IMAGE_TAG: ${{ steps.set_tags.outputs.CODER_BASE_IMAGE_TAG }}
CODER_IMAGE_TAG: ${{ steps.set_tags.outputs.CODER_IMAGE_TAG }}
NEW: ${{ steps.check_deployment.outputs.NEW }}
BUILD: ${{ steps.build_conditionals.outputs.first_or_force_build == 'true' || steps.build_conditionals.outputs.automatic_rebuild == 'true' }}
runs-on: "ubuntu-latest"
steps:
- name: Harden Runner
uses: step-security/harden-runner@95d9a5deda9de15063e7595e9719c11c38c90ae2 # v2.13.2
with:
egress-policy: audit
- name: Checkout
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
with:
fetch-depth: 0
persist-credentials: false
- name: Get PR number, title, and branch name
id: pr_info
run: |
set -euo pipefail
PR_NUMBER=$(gh pr view --json number | jq -r '.number')
PR_TITLE=$(gh pr view --json title | jq -r '.title')
PR_URL=$(gh pr view --json url | jq -r '.url')
{
echo "PR_URL=$PR_URL"
echo "PR_NUMBER=$PR_NUMBER"
echo "PR_TITLE=$PR_TITLE"
} >> "$GITHUB_OUTPUT"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Set required tags
id: set_tags
run: |
set -euo pipefail
echo "CODER_BASE_IMAGE_TAG=$CODER_BASE_IMAGE_TAG" >> "$GITHUB_OUTPUT"
echo "CODER_IMAGE_TAG=$CODER_IMAGE_TAG" >> "$GITHUB_OUTPUT"
env:
CODER_BASE_IMAGE_TAG: ghcr.io/coder/coder-preview-base:pr${{ steps.pr_info.outputs.PR_NUMBER }}
CODER_IMAGE_TAG: ghcr.io/coder/coder-preview:pr${{ steps.pr_info.outputs.PR_NUMBER }}
- name: Set up kubeconfig
run: |
set -euo pipefail
mkdir -p ~/.kube
echo "${{ secrets.PR_DEPLOYMENTS_KUBECONFIG_BASE64 }}" | base64 --decode > ~/.kube/config
chmod 600 ~/.kube/config
export KUBECONFIG=~/.kube/config
- name: Check if the helm deployment already exists
id: check_deployment
run: |
set -euo pipefail
if helm status "pr${PR_NUMBER}" --namespace "pr${PR_NUMBER}" > /dev/null 2>&1; then
echo "Deployment already exists. Skipping deployment."
NEW=false
else
echo "Deployment doesn't exist."
NEW=true
fi
echo "NEW=$NEW" >> "$GITHUB_OUTPUT"
env:
PR_NUMBER: ${{ steps.pr_info.outputs.PR_NUMBER }}
- name: Check changed files
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
id: filter
with:
base: ${{ github.ref }}
filters: |
all:
- "**"
ignored:
- "docs/**"
- "README.md"
- "examples/web-server/**"
- "examples/monitoring/**"
- "examples/lima/**"
- ".github/**"
- "offlinedocs/**"
- ".devcontainer/**"
- "helm/**"
- "*[^g][^o][^.][^s][^u][^m]*"
- "*[^g][^o][^.][^m][^o][^d]*"
- "*[^M][^a][^k][^e][^f][^i][^l][^e]*"
- "scripts/**/*[^D][^o][^c][^k][^e][^r][^f][^i][^l][^e]*"
- "scripts/**/*[^D][^o][^c][^k][^e][^r][^f][^i][^l][^e][.][b][^a][^s][^e]*"
- name: Print number of changed files
run: |
set -euo pipefail
echo "Total number of changed files: ${ALL_COUNT}"
echo "Number of ignored files: ${IGNORED_COUNT}"
env:
ALL_COUNT: ${{ steps.filter.outputs.all_count }}
IGNORED_COUNT: ${{ steps.filter.outputs.ignored_count }}
- name: Build conditionals
id: build_conditionals
run: |
set -euo pipefail
# build if the workflow is manually triggered and the deployment doesn't exist (first build or force rebuild)
echo "first_or_force_build=${{ (github.event_name == 'workflow_dispatch' && steps.check_deployment.outputs.NEW == 'true') || github.event.inputs.build == 'true' }}" >> "$GITHUB_OUTPUT"
# build if the deployment already exist and there are changes in the files that we care about (automatic updates)
echo "automatic_rebuild=${{ steps.check_deployment.outputs.NEW == 'false' && steps.filter.outputs.all_count > steps.filter.outputs.ignored_count }}" >> "$GITHUB_OUTPUT"
comment-pr:
needs: get_info
if: needs.get_info.outputs.BUILD == 'true' || github.event.inputs.deploy == 'true'
runs-on: "ubuntu-latest"
permissions:
pull-requests: write # needed for commenting on PRs
steps:
- name: Harden Runner
uses: step-security/harden-runner@95d9a5deda9de15063e7595e9719c11c38c90ae2 # v2.13.2
with:
egress-policy: audit
- name: Find Comment
uses: peter-evans/find-comment@b30e6a3c0ed37e7c023ccd3f1db5c6c0b0c23aad # v4.0.0
id: fc
with:
issue-number: ${{ needs.get_info.outputs.PR_NUMBER }}
comment-author: "github-actions[bot]"
body-includes: ":rocket:"
direction: last
- name: Comment on PR
id: comment_id
uses: peter-evans/create-or-update-comment@e8674b075228eee787fea43ef493e45ece1004c9 # v5.0.0
with:
comment-id: ${{ steps.fc.outputs.comment-id }}
issue-number: ${{ needs.get_info.outputs.PR_NUMBER }}
edit-mode: replace
body: |
---
:rocket: Deploying PR ${{ needs.get_info.outputs.PR_NUMBER }} ...
---
reactions: eyes
reactions-edit-mode: replace
build:
needs: get_info
# Run build job only if there are changes in the files that we care about or if the workflow is manually triggered with --build flag
if: needs.get_info.outputs.BUILD == 'true'
runs-on: ${{ github.repository_owner == 'coder' && 'depot-ubuntu-22.04-8' || 'ubuntu-latest' }}
permissions:
# Necessary to push docker images to ghcr.io.
packages: write
# This concurrency only cancels build jobs if a new build is triggred. It will avoid cancelling the current deployemtn in case of docs changes.
concurrency:
group: build-${{ github.workflow }}-${{ github.ref }}-${{ needs.get_info.outputs.BUILD }}
cancel-in-progress: true
env:
DOCKER_CLI_EXPERIMENTAL: "enabled"
CODER_IMAGE_TAG: ${{ needs.get_info.outputs.CODER_IMAGE_TAG }}
steps:
- name: Harden Runner
uses: step-security/harden-runner@95d9a5deda9de15063e7595e9719c11c38c90ae2 # v2.13.2
with:
egress-policy: audit
- name: Checkout
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
with:
fetch-depth: 0
persist-credentials: false
- name: Setup Node
uses: ./.github/actions/setup-node
- name: Setup Go
uses: ./.github/actions/setup-go
- name: Setup sqlc
uses: ./.github/actions/setup-sqlc
- name: GHCR Login
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push Linux amd64 Docker image
run: |
set -euo pipefail
go mod download
make gen/mark-fresh
export DOCKER_IMAGE_NO_PREREQUISITES=true
version="$(./scripts/version.sh)"
CODER_IMAGE_BUILD_BASE_TAG="$(CODER_IMAGE_BASE=coder-base ./scripts/image_tag.sh --version "$version")"
export CODER_IMAGE_BUILD_BASE_TAG
make -j build/coder_linux_amd64
./scripts/build_docker.sh \
--arch amd64 \
--target "${CODER_IMAGE_TAG}" \
--version "$version" \
--push \
build/coder_linux_amd64
deploy:
needs: [build, get_info]
# Run deploy job only if build job was successful or skipped
if: |
always() && (needs.build.result == 'success' || needs.build.result == 'skipped') &&
(needs.get_info.outputs.BUILD == 'true' || github.event.inputs.deploy == 'true')
runs-on: "ubuntu-latest"
permissions:
pull-requests: write # needed for commenting on PRs
env:
CODER_IMAGE_TAG: ${{ needs.get_info.outputs.CODER_IMAGE_TAG }}
PR_NUMBER: ${{ needs.get_info.outputs.PR_NUMBER }}
PR_TITLE: ${{ needs.get_info.outputs.PR_TITLE }}
PR_URL: ${{ needs.get_info.outputs.PR_URL }}
PR_HOSTNAME: "pr${{ needs.get_info.outputs.PR_NUMBER }}.${{ secrets.PR_DEPLOYMENTS_DOMAIN }}"
steps:
- name: Harden Runner
uses: step-security/harden-runner@95d9a5deda9de15063e7595e9719c11c38c90ae2 # v2.13.2
with:
egress-policy: audit
- name: Set up kubeconfig
run: |
set -euo pipefail
mkdir -p ~/.kube
echo "${{ secrets.PR_DEPLOYMENTS_KUBECONFIG_BASE64 }}" | base64 --decode > ~/.kube/config
chmod 600 ~/.kube/config
export KUBECONFIG=~/.kube/config
- name: Check if image exists
run: |
set -euo pipefail
foundTag=$(
gh api /orgs/coder/packages/container/coder-preview/versions |
jq -r --arg tag "pr${PR_NUMBER}" '.[] |
select(.metadata.container.tags == [$tag]) |
.metadata.container.tags[0]'
)
if [ -z "$foundTag" ]; then
echo "Image not found"
echo "${CODER_IMAGE_TAG} not found in ghcr.io/coder/coder-preview"
exit 1
else
echo "Image found"
echo "$foundTag tag found in ghcr.io/coder/coder-preview"
fi
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Add DNS record to Cloudflare
if: needs.get_info.outputs.NEW == 'true'
run: |
curl -X POST "https://api.cloudflare.com/client/v4/zones/${{ secrets.PR_DEPLOYMENTS_ZONE_ID }}/dns_records" \
-H "Authorization: Bearer ${{ secrets.PR_DEPLOYMENTS_CLOUDFLARE_API_TOKEN }}" \
-H "Content-Type:application/json" \
--data '{"type":"CNAME","name":"*.'"${PR_HOSTNAME}"'","content":"'"${PR_HOSTNAME}"'","ttl":1,"proxied":false}'
- name: Create PR namespace
if: needs.get_info.outputs.NEW == 'true' || github.event.inputs.deploy == 'true'
run: |
set -euo pipefail
# try to delete the namespace, but don't fail if it doesn't exist
kubectl delete namespace "pr${PR_NUMBER}" || true
kubectl create namespace "pr${PR_NUMBER}"
- name: Checkout
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
with:
persist-credentials: false
- name: Check and Create Certificate
if: needs.get_info.outputs.NEW == 'true' || github.event.inputs.deploy == 'true'
run: |
# Using kubectl to check if a Certificate resource already exists
# we are doing this to avoid letsenrypt rate limits
if ! kubectl get certificate "pr${PR_NUMBER}-tls" -n pr-deployment-certs > /dev/null 2>&1; then
echo "Certificate doesn't exist. Creating a new one."
envsubst < ./.github/pr-deployments/certificate.yaml | kubectl apply -f -
else
echo "Certificate exists. Skipping certificate creation."
fi
echo "Copy certificate from pr-deployment-certs to pr${PR_NUMBER} namespace"
until kubectl get secret "pr${PR_NUMBER}-tls" -n pr-deployment-certs &> /dev/null
do
echo "Waiting for secret pr${PR_NUMBER}-tls to be created..."
sleep 5
done
(
kubectl get secret "pr${PR_NUMBER}-tls" -n pr-deployment-certs -o json |
jq 'del(.metadata.namespace,.metadata.creationTimestamp,.metadata.resourceVersion,.metadata.selfLink,.metadata.uid,.metadata.managedFields)' |
kubectl -n "pr${PR_NUMBER}" apply -f -
)
- name: Set up PostgreSQL database
if: needs.get_info.outputs.NEW == 'true' || github.event.inputs.deploy == 'true'
run: |
helm repo add bitnami https://charts.bitnami.com/bitnami
helm install coder-db bitnami/postgresql \
--namespace "pr${PR_NUMBER}" \
--set image.repository=bitnamilegacy/postgresql \
--set auth.username=coder \
--set auth.password=coder \
--set auth.database=coder \
--set persistence.size=10Gi
kubectl create secret generic coder-db-url -n "pr${PR_NUMBER}" \
--from-literal=url="postgres://coder:coder@coder-db-postgresql.pr${PR_NUMBER}.svc.cluster.local:5432/coder?sslmode=disable"
- name: Create a service account, role, and rolebinding for the PR namespace
if: needs.get_info.outputs.NEW == 'true' || github.event.inputs.deploy == 'true'
run: |
set -euo pipefail
# Create service account, role, rolebinding
envsubst < ./.github/pr-deployments/rbac.yaml | kubectl apply -f -
- name: Create values.yaml
env:
EXPERIMENTS: ${{ github.event.inputs.experiments }}
PR_DEPLOYMENTS_GITHUB_OAUTH_CLIENT_ID: ${{ secrets.PR_DEPLOYMENTS_GITHUB_OAUTH_CLIENT_ID }}
PR_DEPLOYMENTS_GITHUB_OAUTH_CLIENT_SECRET: ${{ secrets.PR_DEPLOYMENTS_GITHUB_OAUTH_CLIENT_SECRET }}
run: |
set -euo pipefail
envsubst < ./.github/pr-deployments/values.yaml > ./pr-deploy-values.yaml
- name: Install/Upgrade Helm chart
run: |
set -euo pipefail
helm dependency update --skip-refresh ./helm/coder
helm upgrade --install "pr${PR_NUMBER}" ./helm/coder \
--namespace "pr${PR_NUMBER}" \
--values ./pr-deploy-values.yaml \
--force
- name: Install coder-logstream-kube
if: needs.get_info.outputs.NEW == 'true' || github.event.inputs.deploy == 'true'
run: |
helm repo add coder-logstream-kube https://helm.coder.com/logstream-kube
helm upgrade --install coder-logstream-kube coder-logstream-kube/coder-logstream-kube \
--namespace "pr${PR_NUMBER}" \
--set url="https://${PR_HOSTNAME}"
- name: Get Coder binary
if: needs.get_info.outputs.NEW == 'true' || github.event.inputs.deploy == 'true'
run: |
set -euo pipefail
DEST="${HOME}/coder"
URL="https://${PR_HOSTNAME}/bin/coder-linux-amd64"
mkdir -p "$(dirname "$DEST")"
COUNT=0
until curl --output /dev/null --silent --head --fail "$URL"; do
printf '.'
sleep 5
COUNT=$((COUNT+1))
if [ "$COUNT" -ge 60 ]; then
echo "Timed out waiting for URL to be available"
exit 1
fi
done
curl -fsSL "$URL" -o "${DEST}"
chmod +x "${DEST}"
"${DEST}" version
sudo mv "${DEST}" /usr/local/bin/coder
- name: Create first user
if: needs.get_info.outputs.NEW == 'true' || github.event.inputs.deploy == 'true'
id: setup_deployment
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -euo pipefail
# create a masked random password 12 characters long
password=$(openssl rand -base64 16 | tr -d "=+/" | cut -c1-12)
# add mask so that the password is not printed to the logs
echo "::add-mask::$password"
echo "password=$password" >> "$GITHUB_OUTPUT"
coder login \
--first-user-username "pr${PR_NUMBER}-admin" \
--first-user-email "pr${PR_NUMBER}@coder.com" \
--first-user-password "$password" \
--first-user-trial=false \
--use-token-as-session \
"https://${PR_HOSTNAME}"
# Create a user for the github.actor
# TODO: update once https://github.com/coder/coder/issues/15466 is resolved
# coder users create \
# --username ${GITHUB_ACTOR} \
# --login-type github
# promote the user to admin role
# coder org members edit-role ${GITHUB_ACTOR} organization-admin
# TODO: update once https://github.com/coder/internal/issues/207 is resolved
- name: Send Slack notification
if: needs.get_info.outputs.NEW == 'true' || github.event.inputs.deploy == 'true'
run: |
curl -s -o /dev/null -X POST -H 'Content-type: application/json' \
-d \
'{
"pr_number": "'"${PR_NUMBER}"'",
"pr_url": "'"${PR_URL}"'",
"pr_title": "'"${PR_TITLE}"'",
"pr_access_url": "'"https://${PR_HOSTNAME}"'",
"pr_username": "'"pr${PR_NUMBER}-admin"'",
"pr_email": "'"pr${PR_NUMBER}@coder.com"'",
"pr_password": "'"${PASSWORD}"'",
"pr_actor": "'"${GITHUB_ACTOR}"'"
}' \
${{ secrets.PR_DEPLOYMENTS_SLACK_WEBHOOK }}
echo "Slack notification sent"
env:
PASSWORD: ${{ steps.setup_deployment.outputs.password }}
- name: Find Comment
uses: peter-evans/find-comment@b30e6a3c0ed37e7c023ccd3f1db5c6c0b0c23aad # v4.0.0
id: fc
with:
issue-number: ${{ env.PR_NUMBER }}
comment-author: "github-actions[bot]"
body-includes: ":rocket:"
direction: last
- name: Comment on PR
uses: peter-evans/create-or-update-comment@e8674b075228eee787fea43ef493e45ece1004c9 # v5.0.0
env:
STATUS: ${{ needs.get_info.outputs.NEW == 'true' && 'Created' || 'Updated' }}
with:
issue-number: ${{ env.PR_NUMBER }}
edit-mode: replace
comment-id: ${{ steps.fc.outputs.comment-id }}
body: |
---
:heavy_check_mark: PR ${{ env.PR_NUMBER }} ${{ env.STATUS }} successfully.
:rocket: Access the credentials [here](${{ secrets.PR_DEPLOYMENTS_SLACK_CHANNEL_URL }}).
---
cc: @${{ github.actor }}
reactions: rocket
reactions-edit-mode: replace
- name: Create template and workspace
if: needs.get_info.outputs.NEW == 'true' || github.event.inputs.deploy == 'true'
run: |
set -euo pipefail
cd .github/pr-deployments/template
coder templates push -y --variable "namespace=pr${PR_NUMBER}" kubernetes
# Create workspace
coder create --template="kubernetes" kube --parameter cpu=2 --parameter memory=4 --parameter home_disk_size=2 -y
coder stop kube -y
-28
View File
@@ -1,28 +0,0 @@
name: release-validation
on:
push:
tags:
- "v*"
permissions:
contents: read
jobs:
network-performance:
runs-on: ubuntu-latest
steps:
- name: Harden Runner
uses: step-security/harden-runner@95d9a5deda9de15063e7595e9719c11c38c90ae2 # v2.13.2
with:
egress-policy: audit
- name: Run Schmoder CI
uses: benc-uk/workflow-dispatch@e2e5e9a103e331dad343f381a29e654aea3cf8fc # v1.2.4
with:
workflow: ci.yaml
repo: coder/schmoder
inputs: '{ "num_releases": "3", "commit": "${{ github.sha }}" }'
token: ${{ secrets.CDRCI_SCHMODER_ACTIONS_TOKEN }}
ref: main
File diff suppressed because it is too large Load Diff
-52
View File
@@ -1,52 +0,0 @@
name: OpenSSF Scorecard
on:
branch_protection_rule:
schedule:
- cron: "27 7 * * 3" # A random time to run weekly
push:
branches: ["main"]
permissions: read-all
jobs:
analysis:
name: Scorecard analysis
runs-on: ubuntu-latest
permissions:
# Needed to upload the results to code-scanning dashboard.
security-events: write
# Needed to publish results and get a badge (see publish_results below).
id-token: write
steps:
- name: Harden Runner
uses: step-security/harden-runner@95d9a5deda9de15063e7595e9719c11c38c90ae2 # v2.13.2
with:
egress-policy: audit
- name: "Checkout code"
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
with:
persist-credentials: false
- name: "Run analysis"
uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3
with:
results_file: results.sarif
results_format: sarif
repo_token: ${{ secrets.GITHUB_TOKEN }}
publish_results: true
# Upload the results as artifacts.
- name: "Upload artifact"
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
with:
name: SARIF file
path: results.sarif
retention-days: 5
# Upload the results to GitHub's code scanning dashboard.
- name: "Upload to code-scanning"
uses: github/codeql-action/upload-sarif@fe4161a26a8629af62121b670040955b330f9af2 # v3.29.5
with:
sarif_file: results.sarif
-178
View File
@@ -1,178 +0,0 @@
name: "security"
permissions:
actions: read
contents: read
on:
workflow_dispatch:
# Uncomment when testing.
# pull_request:
schedule:
# Run every 6 hours Monday-Friday!
- cron: "0 0/6 * * 1-5"
# Cancel in-progress runs for pull requests when developers push
# additional changes
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}-security
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
jobs:
codeql:
permissions:
security-events: write
runs-on: ${{ github.repository_owner == 'coder' && 'depot-ubuntu-22.04-8' || 'ubuntu-latest' }}
steps:
- name: Harden Runner
uses: step-security/harden-runner@95d9a5deda9de15063e7595e9719c11c38c90ae2 # v2.13.2
with:
egress-policy: audit
- name: Checkout
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
with:
persist-credentials: false
- name: Setup Go
uses: ./.github/actions/setup-go
- name: Initialize CodeQL
uses: github/codeql-action/init@fe4161a26a8629af62121b670040955b330f9af2 # v3.29.5
with:
languages: go, javascript
# Workaround to prevent CodeQL from building the dashboard.
- name: Remove Makefile
run: |
rm Makefile
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@fe4161a26a8629af62121b670040955b330f9af2 # v3.29.5
- name: Send Slack notification on failure
if: ${{ failure() }}
run: |
msg="❌ CodeQL Failed\n\nhttps://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"
curl \
-qfsSL \
-X POST \
-H "Content-Type: application/json" \
--data "{\"content\": \"$msg\"}" \
"${{ secrets.SLACK_SECURITY_FAILURE_WEBHOOK_URL }}"
trivy:
permissions:
security-events: write
runs-on: ${{ github.repository_owner == 'coder' && 'depot-ubuntu-22.04-8' || 'ubuntu-latest' }}
steps:
- name: Harden Runner
uses: step-security/harden-runner@95d9a5deda9de15063e7595e9719c11c38c90ae2 # v2.13.2
with:
egress-policy: audit
- name: Checkout
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
with:
fetch-depth: 0
persist-credentials: false
- name: Setup Go
uses: ./.github/actions/setup-go
- name: Setup Node
uses: ./.github/actions/setup-node
- name: Setup sqlc
uses: ./.github/actions/setup-sqlc
- name: Install cosign
uses: ./.github/actions/install-cosign
- name: Install syft
uses: ./.github/actions/install-syft
- name: Install yq
run: go run github.com/mikefarah/yq/v4@v4.44.3
- name: Install mockgen
run: go install go.uber.org/mock/mockgen@v0.5.0
- name: Install protoc-gen-go
run: go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.30
- name: Install protoc-gen-go-drpc
run: go install storj.io/drpc/cmd/protoc-gen-go-drpc@v0.0.34
- name: Install Protoc
run: |
# protoc must be in lockstep with our dogfood Dockerfile or the
# version in the comments will differ. This is also defined in
# ci.yaml.
set -euxo pipefail
cd dogfood/coder
mkdir -p /usr/local/bin
mkdir -p /usr/local/include
DOCKER_BUILDKIT=1 docker build . --target proto -t protoc
protoc_path=/usr/local/bin/protoc
docker run --rm --entrypoint cat protoc /tmp/bin/protoc > $protoc_path
chmod +x $protoc_path
protoc --version
# Copy the generated files to the include directory.
docker run --rm -v /usr/local/include:/target protoc cp -r /tmp/include/google /target/
ls -la /usr/local/include/google/protobuf/
stat /usr/local/include/google/protobuf/timestamp.proto
- name: Build Coder linux amd64 Docker image
id: build
run: |
set -euo pipefail
version="$(./scripts/version.sh)"
image_job="build/coder_${version}_linux_amd64.tag"
# This environment variable force make to not build packages and
# archives (which the Docker image depends on due to technical reasons
# related to concurrent FS writes).
export DOCKER_IMAGE_NO_PREREQUISITES=true
# This environment variables forces scripts/build_docker.sh to build
# the base image tag locally instead of using the cached version from
# the registry.
CODER_IMAGE_BUILD_BASE_TAG="$(CODER_IMAGE_BASE=coder-base ./scripts/image_tag.sh --version "$version")"
export CODER_IMAGE_BUILD_BASE_TAG
# We would like to use make -j here, but it doesn't work with the some recent additions
# to our code generation.
make "$image_job"
echo "image=$(cat "$image_job")" >> "$GITHUB_OUTPUT"
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@b6643a29fecd7f34b3597bc6acb0a98b03d33ff8
with:
image-ref: ${{ steps.build.outputs.image }}
format: sarif
output: trivy-results.sarif
severity: "CRITICAL,HIGH"
- name: Upload Trivy scan results to GitHub Security tab
uses: github/codeql-action/upload-sarif@fe4161a26a8629af62121b670040955b330f9af2 # v3.29.5
with:
sarif_file: trivy-results.sarif
category: "Trivy"
- name: Upload Trivy scan results as an artifact
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
with:
name: trivy
path: trivy-results.sarif
retention-days: 7
- name: Send Slack notification on failure
if: ${{ failure() }}
run: |
msg="❌ Trivy Failed\n\nhttps://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"
curl \
-qfsSL \
-X POST \
-H "Content-Type: application/json" \
--data "{\"content\": \"$msg\"}" \
"${{ secrets.SLACK_SECURITY_FAILURE_WEBHOOK_URL }}"
-143
View File
@@ -1,143 +0,0 @@
name: Stale Issue, Branch and Old Workflows Cleanup
on:
schedule:
# Every day at midnight
- cron: "0 0 * * *"
workflow_dispatch:
permissions:
contents: read
jobs:
issues:
runs-on: ubuntu-latest
permissions:
# Needed to close issues.
issues: write
# Needed to close PRs.
pull-requests: write
steps:
- name: Harden Runner
uses: step-security/harden-runner@95d9a5deda9de15063e7595e9719c11c38c90ae2 # v2.13.2
with:
egress-policy: audit
- name: stale
uses: actions/stale@5f858e3efba33a5ca4407a664cc011ad407f2008 # v10.1.0
with:
stale-issue-label: "stale"
stale-pr-label: "stale"
# days-before-stale: 180
# essentially disabled for now while we work through polish issues
days-before-stale: 3650
# Pull Requests become stale more quickly due to merge conflicts.
# Also, we promote minimizing WIP.
days-before-pr-stale: 7
days-before-pr-close: 3
# We rarely take action in response to the message, so avoid
# cluttering the issue and just close the oldies.
stale-pr-message: ""
stale-issue-message: ""
# Upped from 30 since we have a big tracker and was hitting the limit.
operations-per-run: 60
# Start with the oldest issues, always.
ascending: true
- name: "Close old issues labeled likely-no"
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const thirtyDaysAgo = new Date(new Date().setDate(new Date().getDate() - 30));
console.log(`Looking for issues labeled with 'likely-no' more than 30 days ago, which is after ${thirtyDaysAgo.toISOString()}`);
const issues = await github.rest.issues.listForRepo({
owner: context.repo.owner,
repo: context.repo.repo,
labels: 'likely-no',
state: 'open',
});
console.log(`Found ${issues.data.length} open issues labeled with 'likely-no'`);
for (const issue of issues.data) {
console.log(`Checking issue #${issue.number} created at ${issue.created_at}`);
const timeline = await github.rest.issues.listEventsForTimeline({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue.number,
});
const labelEvent = timeline.data.find(event => event.event === 'labeled' && event.label.name === 'likely-no');
if (labelEvent) {
console.log(`Issue #${issue.number} was labeled with 'likely-no' at ${labelEvent.created_at}`);
if (new Date(labelEvent.created_at) < thirtyDaysAgo) {
console.log(`Issue #${issue.number} is older than 30 days with 'likely-no' label, closing issue.`);
await github.rest.issues.update({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue.number,
state: 'closed',
state_reason: 'not_planned'
});
}
} else {
console.log(`Issue #${issue.number} does not have a 'likely-no' label event in its timeline.`);
}
}
branches:
runs-on: ubuntu-latest
permissions:
# Needed to delete branches.
contents: write
steps:
- name: Harden Runner
uses: step-security/harden-runner@95d9a5deda9de15063e7595e9719c11c38c90ae2 # v2.13.2
with:
egress-policy: audit
- name: Checkout repository
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
with:
persist-credentials: false
- name: Run delete-old-branches-action
uses: beatlabs/delete-old-branches-action@4eeeb8740ff8b3cb310296ddd6b43c3387734588 # v0.0.11
with:
repo_token: ${{ github.token }}
date: "6 months ago"
dry_run: false
delete_tags: false
# extra_protected_branch_regex: ^(foo|bar)$
exclude_open_pr_branches: true
del_runs:
runs-on: ubuntu-latest
permissions:
# Needed to delete workflow runs.
actions: write
steps:
- name: Harden Runner
uses: step-security/harden-runner@95d9a5deda9de15063e7595e9719c11c38c90ae2 # v2.13.2
with:
egress-policy: audit
- name: Delete PR Cleanup workflow runs
uses: Mattraks/delete-workflow-runs@5bf9a1dac5c4d041c029f0a8370ddf0c5cb5aeb7 # v2.1.0
with:
token: ${{ github.token }}
repository: ${{ github.repository }}
retain_days: 30
keep_minimum_runs: 30
delete_workflow_pattern: pr-cleanup.yaml
- name: Delete PR Deploy workflow skipped runs
uses: Mattraks/delete-workflow-runs@5bf9a1dac5c4d041c029f0a8370ddf0c5cb5aeb7 # v2.1.0
with:
token: ${{ github.token }}
repository: ${{ github.repository }}
retain_days: 30
keep_minimum_runs: 30
delete_workflow_pattern: pr-deploy.yaml
-35
View File
@@ -1,35 +0,0 @@
name: Start Workspace On Issue Creation or Comment
on:
issues:
types: [opened]
issue_comment:
types: [created]
permissions:
issues: write
jobs:
comment:
runs-on: ubuntu-latest
if: >-
(github.event_name == 'issue_comment' && contains(github.event.comment.body, '@coder')) ||
(github.event_name == 'issues' && contains(github.event.issue.body, '@coder'))
environment: dev.coder.com
timeout-minutes: 5
steps:
- name: Start Coder workspace
uses: coder/start-workspace-action@f97a681b4cc7985c9eef9963750c7cc6ebc93a19
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
github-username: >-
${{
(github.event_name == 'issue_comment' && github.event.comment.user.login) ||
(github.event_name == 'issues' && github.event.issue.user.login)
}}
coder-url: ${{ secrets.CODER_URL }}
coder-token: ${{ secrets.CODER_TOKEN }}
template-name: ${{ secrets.CODER_TEMPLATE_NAME }}
parameters: |-
AI Prompt: "Use the gh CLI tool to read the details of issue https://github.com/${{ github.repository }}/issues/${{ github.event.issue.number }} and then address it."
Region: us-pittsburgh
-190
View File
@@ -1,190 +0,0 @@
name: AI Triage Automation
on:
issues:
types:
- labeled
workflow_dispatch:
inputs:
issue_url:
description: "GitHub Issue URL to process"
required: true
type: string
template_name:
description: "Coder template to use for workspace"
required: true
default: "coder"
type: string
template_preset:
description: "Template preset to use"
required: false
default: ""
type: string
prefix:
description: "Prefix for workspace name"
required: false
default: "traiage"
type: string
jobs:
traiage:
name: Triage GitHub Issue with Claude Code
runs-on: ubuntu-latest
if: github.event.label.name == 'traiage' || github.event_name == 'workflow_dispatch'
timeout-minutes: 30
env:
CODER_URL: ${{ secrets.TRAIAGE_CODER_URL }}
CODER_SESSION_TOKEN: ${{ secrets.TRAIAGE_CODER_SESSION_TOKEN }}
permissions:
contents: read
issues: write
actions: write
steps:
# This is only required for testing locally using nektos/act, so leaving commented out.
# An alternative is to use a larger or custom image.
# - name: Install Github CLI
# id: install-gh
# run: |
# (type -p wget >/dev/null || (sudo apt update && sudo apt install wget -y)) \
# && sudo mkdir -p -m 755 /etc/apt/keyrings \
# && out=$(mktemp) && wget -nv -O$out https://cli.github.com/packages/githubcli-archive-keyring.gpg \
# && cat $out | sudo tee /etc/apt/keyrings/githubcli-archive-keyring.gpg > /dev/null \
# && sudo chmod go+r /etc/apt/keyrings/githubcli-archive-keyring.gpg \
# && sudo mkdir -p -m 755 /etc/apt/sources.list.d \
# && echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" | sudo tee /etc/apt/sources.list.d/github-cli.list > /dev/null \
# && sudo apt update \
# && sudo apt install gh -y
- name: Determine Inputs
id: determine-inputs
if: always()
env:
GITHUB_ACTOR: ${{ github.actor }}
GITHUB_EVENT_ISSUE_HTML_URL: ${{ github.event.issue.html_url }}
GITHUB_EVENT_NAME: ${{ github.event_name }}
GITHUB_EVENT_USER_ID: ${{ github.event.sender.id }}
GITHUB_EVENT_USER_LOGIN: ${{ github.event.sender.login }}
INPUTS_ISSUE_URL: ${{ inputs.issue_url }}
INPUTS_TEMPLATE_NAME: ${{ inputs.template_name || 'coder' }}
INPUTS_TEMPLATE_PRESET: ${{ inputs.template_preset || ''}}
INPUTS_PREFIX: ${{ inputs.prefix || 'traiage' }}
GH_TOKEN: ${{ github.token }}
run: |
echo "Using template name: ${INPUTS_TEMPLATE_NAME}"
echo "template_name=${INPUTS_TEMPLATE_NAME}" >> "${GITHUB_OUTPUT}"
echo "Using template preset: ${INPUTS_TEMPLATE_PRESET}"
echo "template_preset=${INPUTS_TEMPLATE_PRESET}" >> "${GITHUB_OUTPUT}"
echo "Using prefix: ${INPUTS_PREFIX}"
echo "prefix=${INPUTS_PREFIX}" >> "${GITHUB_OUTPUT}"
# For workflow_dispatch, use the actor who triggered it
# For issues events, use the issue author.
if [[ "${GITHUB_EVENT_NAME}" == "workflow_dispatch" ]]; then
if ! GITHUB_USER_ID=$(gh api "users/${GITHUB_ACTOR}" --jq '.id'); then
echo "::error::Failed to get GitHub user ID for actor ${GITHUB_ACTOR}"
exit 1
fi
echo "Using workflow_dispatch actor: ${GITHUB_ACTOR} (ID: ${GITHUB_USER_ID})"
echo "github_user_id=${GITHUB_USER_ID}" >> "${GITHUB_OUTPUT}"
echo "github_username=${GITHUB_ACTOR}" >> "${GITHUB_OUTPUT}"
echo "Using issue URL: ${INPUTS_ISSUE_URL}"
echo "issue_url=${INPUTS_ISSUE_URL}" >> "${GITHUB_OUTPUT}"
exit 0
elif [[ "${GITHUB_EVENT_NAME}" == "issues" ]]; then
GITHUB_USER_ID=${GITHUB_EVENT_USER_ID}
echo "Using issue author: ${GITHUB_EVENT_USER_LOGIN} (ID: ${GITHUB_USER_ID})"
echo "github_user_id=${GITHUB_USER_ID}" >> "${GITHUB_OUTPUT}"
echo "github_username=${GITHUB_EVENT_USER_LOGIN}" >> "${GITHUB_OUTPUT}"
echo "Using issue URL: ${GITHUB_EVENT_ISSUE_HTML_URL}"
echo "issue_url=${GITHUB_EVENT_ISSUE_HTML_URL}" >> "${GITHUB_OUTPUT}"
exit 0
else
echo "::error::Unsupported event type: ${GITHUB_EVENT_NAME}"
exit 1
fi
- name: Verify push access
env:
GITHUB_REPOSITORY: ${{ github.repository }}
GH_TOKEN: ${{ github.token }}
GITHUB_USERNAME: ${{ steps.determine-inputs.outputs.github_username }}
GITHUB_USER_ID: ${{ steps.determine-inputs.outputs.github_user_id }}
run: |
# Query the actors permission on this repo
can_push="$(gh api "/repos/${GITHUB_REPOSITORY}/collaborators/${GITHUB_USERNAME}/permission" --jq '.user.permissions.push')"
if [[ "${can_push}" != "true" ]]; then
echo "::error title=Access Denied::${GITHUB_USERNAME} does not have push access to ${GITHUB_REPOSITORY}"
exit 1
fi
- name: Extract context key and description from issue
id: extract-context
env:
ISSUE_URL: ${{ steps.determine-inputs.outputs.issue_url }}
GH_TOKEN: ${{ github.token }}
run: |
issue_number="$(gh issue view "${ISSUE_URL}" --json number --jq '.number')"
context_key="gh-${issue_number}"
TASK_PROMPT=$(cat <<EOF
Fix ${ISSUE_URL}
1. Use the gh CLI to read the issue description and comments.
2. Think carefully and try to understand the root cause. If the issue is unclear or not well defined, ask me to clarify and provide more information.
3. Write a proposed implementation plan to PLAN.md for me to review before starting implementation. Your plan should use TDD and only make the minimal changes necessary to fix the root cause.
4. When I approve your plan, start working on it. If you encounter issues with the plan, ask me for clarification and update the plan as required.
5. When you have finished implementation according to the plan, commit and push your changes, and create a PR using the gh CLI for me to review.
EOF
)
echo "context_key=${context_key}" >> "${GITHUB_OUTPUT}"
{
echo "TASK_PROMPT<<EOF"
echo "${TASK_PROMPT}"
echo "EOF"
} >> "${GITHUB_OUTPUT}"
- name: Checkout repository
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
with:
fetch-depth: 1
path: ./.github/actions/create-task-action
persist-credentials: false
ref: main
repository: coder/create-task-action
- name: Create Coder Task
id: create_task
uses: ./.github/actions/create-task-action
with:
coder-url: ${{ secrets.TRAIAGE_CODER_URL }}
coder-token: ${{ secrets.TRAIAGE_CODER_SESSION_TOKEN }}
coder-organization: "default"
coder-template-name: coder
coder-template-preset: ${{ steps.determine-inputs.outputs.template_preset }}
coder-task-name-prefix: gh-coder
coder-task-prompt: ${{ steps.extract-context.outputs.task_prompt }}
github-user-id: ${{ steps.determine-inputs.outputs.github_user_id }}
github-token: ${{ github.token }}
github-issue-url: ${{ steps.determine-inputs.outputs.issue_url }}
comment-on-issue: ${{ startsWith(steps.determine-inputs.outputs.issue_url, format('{0}/{1}', github.server_url, github.repository)) }}
- name: Write outputs
env:
TASK_CREATED: ${{ steps.create_task.outputs.task-created }}
TASK_NAME: ${{ steps.create_task.outputs.task-name }}
TASK_URL: ${{ steps.create_task.outputs.task-url }}
run: |
{
echo "**Task created:** ${TASK_CREATED}"
echo "**Task name:** ${TASK_NAME}"
echo "**Task URL**: ${TASK_URL}"
} >> "${GITHUB_STEP_SUMMARY}"
-54
View File
@@ -1,54 +0,0 @@
[default]
extend-ignore-identifiers-re = ["gho_.*"]
extend-ignore-re = ["(#|//)\\s*spellchecker:ignore-next-line\\n.*"]
[default.extend-identifiers]
alog = "alog"
Jetbrains = "JetBrains"
IST = "IST"
MacOS = "macOS"
AKS = "AKS"
O_WRONLY = "O_WRONLY"
AIBridge = "AI Bridge"
[default.extend-words]
AKS = "AKS"
# do as sudo replacement
doas = "doas"
darcula = "darcula"
Hashi = "Hashi"
trialer = "trialer"
encrypter = "encrypter"
# as in helsinki
hel = "hel"
# this is used as proto node
pn = "pn"
# typos doesn't like the EDE in TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA
EDE = "EDE"
# HELO is an SMTP command
HELO = "HELO"
LKE = "LKE"
byt = "byt"
typ = "typ"
Inferrable = "Inferrable"
[files]
extend-exclude = [
"**.svg",
"**.png",
"**.lock",
"go.sum",
"go.mod",
# These files contain base64 strings that confuse the detector
"**XService**.ts",
"**identity.go",
"**/*_test.go",
"**/*.test.tsx",
"**/pnpm-lock.yaml",
"tailnet/testdata/**",
"site/src/pages/SetupPage/countries.tsx",
"provisioner/terraform/testdata/**",
# notifications' golden files confuse the detector because of quoted-printable encoding
"coderd/notifications/testdata/**",
"agent/agentcontainers/testdata/devcontainercli/**",
]
-52
View File
@@ -1,52 +0,0 @@
name: weekly-docs
# runs every monday at 9 am
on:
schedule:
- cron: "0 9 * * 1"
workflow_dispatch: # allows to run manually for testing
pull_request:
branches:
- main
paths:
- "docs/**"
permissions:
contents: read
jobs:
check-docs:
# later versions of Ubuntu have disabled unprivileged user namespaces, which are required by the action
runs-on: ubuntu-22.04
permissions:
pull-requests: write # required to post PR review comments by the action
steps:
- name: Harden Runner
uses: step-security/harden-runner@95d9a5deda9de15063e7595e9719c11c38c90ae2 # v2.13.2
with:
egress-policy: audit
- name: Checkout
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
with:
persist-credentials: false
- name: Check Markdown links
uses: umbrelladocs/action-linkspector@652f85bc57bb1e7d4327260decc10aa68f7694c3 # v1.4.0
id: markdown-link-check
# checks all markdown files from /docs including all subfolders
with:
reporter: github-pr-review
config_file: ".github/.linkspector.yml"
fail_on_error: "true"
filter_mode: "file"
- name: Send Slack notification
if: failure() && github.event_name == 'schedule'
run: |
curl \
-X POST \
-H 'Content-type: application/json' \
-d '{"msg":"Broken links found in the documentation. Please check the logs at '"${LOGS_URL}"'"}' "${{ secrets.DOCS_LINK_SLACK_WEBHOOK }}"
echo "Sent Slack notification"
env:
LOGS_URL: https://github.com/coder/coder/actions/runs/${{ github.run_id }}
-4
View File
@@ -1,4 +0,0 @@
rules:
cache-poisoning:
ignore:
- "ci.yaml:184"
+21 -86
View File
@@ -1,99 +1,34 @@
# Common ignore patterns, these rules applies in both root and subdirectories.
.DS_Store
###############################################################################
# NOTICE #
# If you change this file, kindly copy-pasta your change into .prettierignore #
# and .eslintignore as well. See the following discussions to understand why #
# we have to resort to this duplication (at least for now): #
# #
# https://github.com/prettier/prettier/issues/8048 #
# https://github.com/prettier/prettier/issues/8506 #
# https://github.com/prettier/prettier/issues/8679 #
###############################################################################
node_modules
vendor
.eslintcache
.gitpod.yml
.idea
**/*.swp
gotests.coverage
gotests.xml
gotests_stats.json
gotests.json
node_modules/
vendor/
yarn-error.log
# Test output files
test-output/
# VSCode settings.
**/.vscode/*
# Allow VSCode recommendations and default settings in project root.
!/.vscode/extensions.json
!/.vscode/settings.json
# Allow code snippets
!/.vscode/*.code-snippets
# Front-end ignore patterns.
# Front-end ignore
.next/
site/build-storybook.log
site/coverage/
site/.eslintcache
site/.next/
site/node_modules/
site/storybook-static/
site/test-results/*
site/e2e/test-results/*
site/e2e/states/*.json
site/e2e/.auth.json
site/playwright-report/*
site/.swc
# Make target for updating generated/golden files (any dir).
.gen
.gen-golden
site/test-results/
site/yarn-error.log
coverage/
# Build
bin/
build/
dist/
out/
# Bundle analysis
site/stats/
site/out/
*.tfstate
*.tfstate.backup
*.tfplan
*.lock.hcl
.terraform/
!coderd/testdata/parameters/modules/.terraform/
!provisioner/terraform/testdata/modules-source-caching/.terraform/
**/.coderv2/*
**/__debug_bin
# direnv
.envrc
.direnv
*.test
# Loadtesting
./scaletest/terraform/.terraform
./scaletest/terraform/.terraform.lock.hcl
scaletest/terraform/secrets.tfvars
.terraform.tfstate.*
# Nix
result
# Data dumps from unit tests
**/*.test.sql
# Filebrowser.db
**/filebrowser.db
# pnpm
.pnpm-store/
# Zed
.zed_server
# dlv debug binaries for go tests
__debug_bin*
**/.claude/settings.local.json
# Local agent configuration
AGENTS.local.md
/.env
# Ignore plans written by AI agents.
PLAN.md
-278
View File
@@ -1,278 +0,0 @@
# See https://golangci-lint.run/usage/configuration/
# Over time we should try tightening some of these.
linters-settings:
dupl:
# goal: 100
threshold: 412
exhaustruct:
include:
# Gradually extend to cover more of the codebase.
- 'httpmw\.\w+'
# We want to enforce all values are specified when inserting or updating
# a database row. Ref: #9936
- 'github.com/coder/coder/v2/coderd/database\.[^G][^e][^t]\w+Params'
gocognit:
min-complexity: 300
goconst:
min-len: 4 # Min length of string consts (def 3).
min-occurrences: 3 # Min number of const occurrences (def 3).
gocritic:
enabled-checks:
# - appendAssign
# - appendCombine
# - assignOp
# - badCall
- badLock
- badRegexp
- boolExprSimplify
# - builtinShadow
- builtinShadowDecl
# - commentedOutCode
- commentedOutImport
- deferUnlambda
# - deprecatedComment
# - docStub
- dupImport
# - elseif
- emptyFallthrough
# - emptyStringTest
# - equalFold
# - evalOrder
# - exitAfterDefer
# - exposedSyncMutex
# - filepathJoin
- hexLiteral
# - httpNoBody
# - hugeParam
# - ifElseChain
# - importShadow
- indexAlloc
- initClause
- methodExprCall
# - nestingReduce
- nilValReturn
# - octalLiteral
# - paramTypeCombine
# - preferStringWriter
# - preferWriteByte
# - ptrToRefParam
# - rangeExprCopy
# - rangeValCopy
- regexpPattern
# - regexpSimplify
- ruleguard
# - sloppyReassign
- sortSlice
- sprintfQuotedString
- sqlQuery
# - stringConcatSimplify
# - stringXbytes
# - suspiciousSorting
- truncateCmp
- typeAssertChain
# - typeDefFirst
# - typeUnparen
# - unlabelStmt
# - unlambda
# - unnamedResult
# - unnecessaryBlock
# - unnecessaryDefer
# - unslice
- weakCond
# - whyNoLint
# - wrapperFunc
# - yodaStyleExpr
settings:
ruleguard:
failOn: all
rules: "${configDir}/scripts/rules.go"
staticcheck:
# https://staticcheck.io/docs/options#checks
# We disable SA1019 because it gets angry about our usage of xerrors. We
# intentionally xerrors because stack frame support didn't make it into the
# stdlib port.
checks: ["all", "-SA1019"]
goimports:
local-prefixes: coder.com,cdr.dev,go.coder.com,github.com/cdr,github.com/coder
importas:
no-unaliased: true
misspell:
locale: US
ignore-words:
- trialer
nestif:
# goal: 10
min-complexity: 20
revive:
# see https://github.com/mgechev/revive#available-rules for details.
ignore-generated-header: true
severity: warning
rules:
- name: atomic
- name: bare-return
- name: blank-imports
- name: bool-literal-in-expr
- name: call-to-gc
- name: confusing-naming
- name: confusing-results
- name: constant-logical-expr
- name: context-as-argument
- name: context-keys-type
- name: deep-exit
- name: defer
- name: dot-imports
- name: duplicated-imports
- name: early-return
- name: empty-block
- name: empty-lines
- name: error-naming
- name: error-return
- name: error-strings
- name: errorf
- name: exported
- name: flag-parameter
- name: get-return
- name: identical-branches
- name: if-return
- name: import-shadowing
- name: increment-decrement
- name: indent-error-flow
# - name: modifies-parameter
- name: modifies-value-receiver
- name: package-comments
- name: range
- name: receiver-naming
- name: redefines-builtin-id
- name: string-of-int
- name: struct-tag
- name: superfluous-else
- name: time-naming
- name: unconditional-recursion
- name: unexported-naming
- name: unexported-return
- name: unhandled-error
- name: unnecessary-stmt
- name: unreachable-code
- name: unused-parameter
exclude: "**/*_test.go"
- name: unused-receiver
- name: var-declaration
- name: var-naming
- name: waitgroup-by-value
usetesting:
# Only os-setenv is enabled because we migrated to usetesting from another linter that
# only covered os-setenv.
os-setenv: true
os-create-temp: false
os-mkdir-temp: false
os-temp-dir: false
os-chdir: false
context-background: false
context-todo: false
# irrelevant as of Go v1.22: https://go.dev/blog/loopvar-preview
govet:
disable:
- loopclosure
gosec:
excludes:
# Implicit memory aliasing of items from a range statement (irrelevant as of Go v1.22)
- G601
issues:
exclude-dirs:
- node_modules
- .git
exclude-files:
- scripts/rules.go
# Rules listed here: https://github.com/securego/gosec#available-rules
exclude-rules:
- path: _test\.go
linters:
# We use assertions rather than explicitly checking errors in tests
- errcheck
- forcetypeassert
- exhaustruct # This is unhelpful in tests.
- path: scripts/*
linters:
- exhaustruct
- path: scripts/rules.go
linters:
- ALL
fix: true
max-issues-per-linter: 0
max-same-issues: 0
run:
timeout: 10m
# Over time, add more and more linters from
# https://golangci-lint.run/usage/linters/ as the code improves.
linters:
disable-all: true
enable:
- asciicheck
- bidichk
- bodyclose
- dogsled
- errcheck
- errname
- errorlint
- exhaustruct
- forcetypeassert
- gocritic
# gocyclo is may be useful in the future when we start caring
# about testing complexity, but for the time being we should
# create a good culture around cognitive complexity.
# - gocyclo
- gocognit
- nestif
- goimports
- gomodguard
- gosec
- gosimple
- govet
- importas
- ineffassign
- makezero
- misspell
- nilnil
- noctx
- paralleltest
- revive
# These don't work until the following issue is solved.
# https://github.com/golangci/golangci-lint/issues/2649
# - rowserrcheck
# - sqlclosecheck
# - structcheck
# - wastedassign
- staticcheck
# In Go, it's possible for a package to test it's internal functionality
# without testing any exported functions. This is enabled to promote
# decomposing a package before testing it's internals. A function caller
# should be able to test most of the functionality from exported functions.
#
# There are edge-cases to this rule, but they should be carefully considered
# to avoid structural inconsistency.
- testpackage
- tparallel
- typecheck
- unconvert
- unused
- usetesting
- dupl
+255
View File
@@ -0,0 +1,255 @@
# See https://golangci-lint.run/usage/configuration/
# Over time we should try tightening some of these.
linters-settings:
gocognit:
min-complexity: 46 # Min code complexity (def 30).
goconst:
min-len: 4 # Min length of string consts (def 3).
min-occurrences: 3 # Min number of const occurrences (def 3).
gocritic:
enabled-checks:
# - appendAssign
# - appendCombine
- argOrder
# - assignOp
# - badCall
- badCond
- badLock
- badRegexp
- boolExprSimplify
# - builtinShadow
- builtinShadowDecl
- captLocal
- caseOrder
- codegenComment
# - commentedOutCode
- commentedOutImport
# - commentFormatting
- defaultCaseOrder
- deferUnlambda
# - deprecatedComment
# - docStub
- dupArg
- dupBranchBody
- dupCase
- dupImport
- dupSubExpr
# - elseif
- emptyFallthrough
# - emptyStringTest
# - equalFold
# - evalOrder
# - exitAfterDefer
# - exposedSyncMutex
# - filepathJoin
- flagDeref
- flagName
- hexLiteral
# - httpNoBody
# - hugeParam
# - ifElseChain
# - importShadow
- indexAlloc
- initClause
# - ioutilDeprecated
- mapKey
- methodExprCall
# - nestingReduce
- newDeref
- nilValReturn
# - octalLiteral
- offBy1
# - paramTypeCombine
# - preferStringWriter
# - preferWriteByte
# - ptrToRefParam
# - rangeExprCopy
# - rangeValCopy
- regexpMust
- regexpPattern
# - regexpSimplify
- ruleguard
- singleCaseSwitch
- sloppyLen
# - sloppyReassign
- sloppyTypeAssert
- sortSlice
# - sprintfQuotedString
- sqlQuery
# - stringConcatSimplify
# - stringXbytes
# - suspiciousSorting
- switchTrue
- truncateCmp
- typeAssertChain
# - typeDefFirst
- typeSwitchVar
# - typeUnparen
- underef
# - unlabelStmt
# - unlambda
# - unnamedResult
# - unnecessaryBlock
# - unnecessaryDefer
# - unslice
- valSwap
- weakCond
# - whyNoLint
# - wrapperFunc
# - yodaStyleExpr
settings:
ruleguard:
failOn: all
rules: rules.go
goimports:
local-prefixes: coder.com,cdr.dev,go.coder.com,github.com/cdr,github.com/coder
gocyclo:
min-complexity: 50
importas:
no-unaliased: true
misspell:
locale: US
nestif:
min-complexity: 4 # Min complexity of if statements (def 5, goal 4)
revive:
# see https://github.com/mgechev/revive#available-rules for details.
ignore-generated-header: true
severity: warning
rules:
- name: atomic
- name: bare-return
- name: blank-imports
- name: bool-literal-in-expr
- name: call-to-gc
- name: confusing-naming
- name: confusing-results
- name: constant-logical-expr
- name: context-as-argument
- name: context-keys-type
- name: deep-exit
- name: defer
- name: dot-imports
- name: duplicated-imports
- name: early-return
- name: empty-block
- name: empty-lines
- name: error-naming
- name: error-return
- name: error-strings
- name: errorf
- name: exported
- name: flag-parameter
- name: get-return
- name: identical-branches
- name: if-return
- name: import-shadowing
- name: increment-decrement
- name: indent-error-flow
- name: modifies-parameter
- name: modifies-value-receiver
- name: package-comments
- name: range
- name: range-val-address
- name: range-val-in-closure
- name: receiver-naming
- name: redefines-builtin-id
- name: string-of-int
- name: struct-tag
- name: superfluous-else
- name: time-naming
- name: unconditional-recursion
- name: unexported-naming
- name: unexported-return
- name: unhandled-error
- name: unnecessary-stmt
- name: unreachable-code
- name: unused-parameter
- name: unused-receiver
- name: var-declaration
- name: var-naming
- name: waitgroup-by-value
varnamelen:
ignore-names:
- err
- rw
- r
- i
- db
# Optional list of variable declarations that should be ignored completely. (defaults to empty list)
# Entries must be in the form of "<variable name> <type>" or "<variable name> *<type>" for
# variables, or "const <name>" for constants.
ignore-decls:
- rw http.ResponseWriter
- r *http.Request
- t testing.T
issues:
# Rules listed here: https://github.com/securego/gosec#available-rules
exclude-rules:
- path: _test\.go
linters:
# We use assertions rather than explicitly checking errors in tests
- errcheck
fix: true
max-issues-per-linter: 0
max-same-issues: 0
run:
concurrency: 4
skip-dirs:
- node_modules
timeout: 5m
# Over time, add more and more linters from
# https://golangci-lint.run/usage/linters/ as the code improves.
linters:
disable-all: true
enable:
- asciicheck
- bidichk
- bodyclose
- deadcode
- dogsled
- errcheck
- errname
- errorlint
- exportloopref
- forcetypeassert
- gocritic
- gocyclo
- goimports
- gomodguard
- gosec
- gosimple
- govet
- importas
- ineffassign
- makezero
- misspell
- nilnil
- noctx
- paralleltest
- revive
- rowserrcheck
- sqlclosecheck
- staticcheck
- structcheck
- tenv
- testpackage
- tparallel
- typecheck
- unconvert
- unused
- varcheck
- varnamelen
- wastedassign
+49
View File
@@ -0,0 +1,49 @@
archives:
- id: coder
builds:
- coder
files:
- README.md
before:
hooks:
- go mod tidy
- rm -f site/out/bin/coder*
builds:
- id: coder-slim
dir: cmd/coder
ldflags: ["-s -w"]
env: [CGO_ENABLED=0]
goos: [darwin, linux, windows]
goarch: [amd64, arm64]
hooks:
# The "trimprefix" appends ".exe" on Windows.
post: |
cp {{.Path}} site/out/bin/coder-{{ .Os }}-{{ .Arch }}{{ trimprefix .Name "coder" }}
- id: coder
dir: cmd/coder
flags: [-tags=embed]
ldflags: ["-s -w"]
env: [CGO_ENABLED=0]
goos: [darwin, linux, windows]
goarch: [amd64, arm64]
nfpms:
- vendor: Coder
homepage: https://coder.com
maintainer: Coder <support@coder.com>
description: |
Provision development environments with infrastructure with code
formats:
- apk
- deb
- rpm
suggests:
- postgresql
builds:
- coder
release:
ids: [coder]
-3
View File
@@ -1,3 +0,0 @@
{
"ignores": ["PLAN.md"],
}
-31
View File
@@ -1,31 +0,0 @@
// Example markdownlint configuration with all properties set to their default value
{
"MD010": { "spaces_per_tab": 4}, // No hard tabs: we use 4 spaces per tab
"MD013": false, // Line length: we are not following a strict line lnegth in markdown files
"MD024": { "siblings_only": true }, // Multiple headings with the same content:
"MD033": false, // Inline HTML: we use it in some places
"MD034": false, // Bare URL: we use it in some places in generated docs e.g.
// codersdk/deployment.go L597, L1177, L2287, L2495, L2533
// codersdk/workspaceproxy.go L196, L200-L201
// coderd/tracing/exporter.go L26
// cli/exp_scaletest.go L-9
"MD041": false, // First line in file should be a top level heading: All of our changelogs do not start with a top level heading
// TODO: We need to update /home/coder/repos/coder/coder/scripts/release/generate_release_notes.sh to generate changelogs that follow this rule
"MD052": false, // Image reference: Not a valid reference in generated docs
// docs/reference/cli/server.md L628
"MD055": false, // Table pipe style: Some of the generated tables do not have ending pipes
// docs/reference/api/schema.md
// docs/reference/api/templates.md
// docs/reference/cli/server.md
"MD056": false // Table column count: Some of the auto-generated tables have issues. TODO: This is probably because of splitting cell content to multiple lines.
// docs/reference/api/schema.md
// docs/reference/api/templates.md
}
-36
View File
@@ -1,36 +0,0 @@
{
"mcpServers": {
"go-language-server": {
"type": "stdio",
"command": "go",
"args": [
"run",
"github.com/isaacphi/mcp-language-server@latest",
"-workspace",
"./",
"-lsp",
"go",
"--",
"run",
"golang.org/x/tools/gopls@latest"
],
"env": {}
},
"typescript-language-server": {
"type": "stdio",
"command": "go",
"args": [
"run",
"github.com/isaacphi/mcp-language-server@latest",
"-workspace",
"./site/",
"-lsp",
"pnpx",
"--",
"typescript-language-server",
"--stdio"
],
"env": {}
}
}
}
-18
View File
@@ -1,18 +0,0 @@
# This config file is used in conjunction with `.editorconfig` to specify
# formatting for prettier-supported files. See `.editorconfig` and
# `site/.editorconfig` for whitespace formatting options.
printWidth: 80
proseWrap: always
trailingComma: all
useTabs: true
tabWidth: 2
overrides:
- files:
- README.md
- docs/reference/api/**/*.md
- docs/reference/cli/**/*.md
- docs/changelogs/*.md
- .github/**/*.{yaml,yml,toml}
- scripts/**/*.{yaml,yml,toml}
options:
proseWrap: preserve
-8
View File
@@ -1,8 +0,0 @@
// Replace all NullTime with string
replace github.com/coder/coder/v2/codersdk.NullTime string
// Prevent swaggo from rendering enums for time.Duration
replace time.Duration int64
// Do not expose "echo" provider
replace github.com/coder/coder/v2/codersdk.ProvisionerType string
// Do not render netip.Addr
replace netip.Addr string
+10 -14
View File
@@ -1,16 +1,12 @@
{
"recommendations": [
"biomejs.biome",
"bradlc.vscode-tailwindcss",
"DavidAnson.vscode-markdownlint",
"EditorConfig.EditorConfig",
"emeraldwalk.runonsave",
"foxundermoon.shell-format",
"github.vscode-codeql",
"golang.go",
"hashicorp.terraform",
"redhat.vscode-yaml",
"tekumara.typos-vscode",
"zxh404.vscode-proto3"
]
"recommendations": [
"golang.go",
"hashicorp.terraform",
"esbenp.prettier-vscode",
"foxundermoon.shell-format",
"emeraldwalk.runonsave",
"zxh404.vscode-proto3",
"redhat.vscode-yaml",
"streetsidesoftware.code-spell-checker"
]
}
-45
View File
@@ -1,45 +0,0 @@
{
// For info about snippets, visit https://code.visualstudio.com/docs/editor/userdefinedsnippets
// https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax#alerts
"alert": {
"prefix": "#alert",
"body": [
"> [!${1|CAUTION,IMPORTANT,NOTE,TIP,WARNING|}]",
"> ${TM_SELECTED_TEXT:${2:add info here}}\n"
],
"description": "callout admonition caution important note tip warning"
},
"fenced code block": {
"prefix": "#codeblock",
"body": ["```${1|apache,bash,console,diff,Dockerfile,env,go,hcl,ini,json,lisp,md,powershell,shell,sql,text,tf,tsx,yaml|}", "${TM_SELECTED_TEXT}$0", "```"],
"description": "fenced code block"
},
"image": {
"prefix": "#image",
"body": "![${TM_SELECTED_TEXT:${1:alt}}](${2:url})$0",
"description": "image"
},
"premium-feature": {
"prefix": "#premium-feature",
"body": [
"> [!NOTE]\n",
"> ${1:feature} ${2|is,are|} an Enterprise and Premium feature. [Learn more](https://coder.com/pricing#compare-plans).\n"
]
},
"tabs": {
"prefix": "#tabs",
"body": [
"<div class=\"tabs\">\n",
"${1:optional description}\n",
"## ${2:tab title}\n",
"${TM_SELECTED_TEXT:${3:first tab content}}\n",
"## ${4:tab title}\n",
"${5:second tab content}\n",
"## ${6:tab title}\n",
"${7:third tab content}\n",
"</div>\n"
],
"description": "tabs"
}
}
+84 -64
View File
@@ -1,66 +1,86 @@
{
"emeraldwalk.runonsave": {
"commands": [
{
"match": "database/queries/*.sql",
"cmd": "make gen"
},
{
"match": "provisionerd/proto/provisionerd.proto",
"cmd": "make provisionerd/proto/provisionerd.pb.go"
}
]
},
"search.exclude": {
"**.pb.go": true,
"**/*.gen.json": true,
"**/testdata/*": true,
"coderd/apidoc/**": true,
"docs/reference/api/*.md": true,
"docs/reference/cli/*.md": true,
"docs/templates/*.md": true,
"LICENSE": true,
"scripts/metricsdocgen/metrics": true,
"site/out/**": true,
"site/storybook-static/**": true,
"**.map": true,
"pnpm-lock.yaml": true
},
// Ensure files always have a newline.
"files.insertFinalNewline": true,
"go.lintTool": "golangci-lint",
"go.lintFlags": ["--fast"],
"go.coverageDecorator": {
"type": "gutter",
"coveredGutterStyle": "blockgreen",
"uncoveredGutterStyle": "blockred"
},
// The codersdk is used by coderd another other packages extensively.
// To reduce redundancy in tests, it's covered by other packages.
// Since package coverage pairing can't be defined, all packages cover
// all other packages.
"go.testFlags": ["-short", "-coverpkg=./..."],
// We often use a version of TypeScript that's ahead of the version shipped
// with VS Code.
"typescript.tsdk": "./site/node_modules/typescript/lib",
// Playwright tests in VSCode will open a browser to live "view" the test.
"playwright.reuseBrowser": true,
"[javascript][javascriptreact][json][jsonc][typescript][typescriptreact]": {
"editor.defaultFormatter": "biomejs.biome",
"editor.codeActionsOnSave": {
"source.fixAll.biome": "explicit"
// "source.organizeImports.biome": "explicit"
}
},
"tailwindCSS.classFunctions": ["cva", "cn"],
"[css][html][markdown][yaml]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"typos.config": ".github/workflows/typos.toml",
"[markdown]": {
"editor.defaultFormatter": "DavidAnson.vscode-markdownlint"
},
"biome.lsp.bin": "site/node_modules/.bin/biome"
"files.exclude": {
"**/node_modules": true
},
"go.lintTool": "golangci-lint",
"go.lintFlags": ["--fast"],
"go.lintOnSave": "package",
"go.coverOnSave": true,
// The codersdk is used by coderd another other packages extensively.
// To reduce redundancy in tests, it's covered by other packages.
"go.testFlags": ["-short", "-coverpkg=./.,github.com/coder/coder/codersdk"],
"go.coverageDecorator": {
"type": "gutter",
"coveredHighlightColor": "rgba(64,128,128,0.5)",
"uncoveredHighlightColor": "rgba(128,64,64,0.25)",
"coveredBorderColor": "rgba(64,128,128,0.5)",
"uncoveredBorderColor": "rgba(128,64,64,0.25)",
"coveredGutterStyle": "blockgreen",
"uncoveredGutterStyle": "blockred"
},
"emeraldwalk.runonsave": {
"commands": [
{
"match": "database/query.sql",
"cmd": "make gen"
}
]
},
"cSpell.words": [
"cliui",
"coderd",
"coderdtest",
"codersdk",
"drpc",
"drpcconn",
"drpcmux",
"drpcserver",
"fatih",
"goarch",
"goleak",
"gossh",
"hashicorp",
"hclsyntax",
"httpmw",
"idtoken",
"Iflag",
"incpatch",
"isatty",
"Jobf",
"kirsle",
"ldflags",
"manifoldco",
"mattn",
"mitchellh",
"moby",
"nfpms",
"nhooyr",
"nolint",
"nosec",
"ntqry",
"oneof",
"parameterscopeid",
"pqtype",
"promptui",
"protobuf",
"provisionerd",
"provisionersdk",
"ptty",
"ptytest",
"retrier",
"sdkproto",
"stretchr",
"TCGETS",
"tcpip",
"TCSETS",
"tfexec",
"tfstate",
"trimprefix",
"unconvert",
"Untar",
"webrtc",
"xerrors",
"yamux"
],
"eslint.workingDirectories": ["./site"]
}
-1
View File
@@ -1 +0,0 @@
CLAUDE.md
-216
View File
@@ -1,216 +0,0 @@
# Coder Development Guidelines
You are an experienced, pragmatic software engineer. You don't over-engineer a solution when a simple one is possible.
Rule #1: If you want exception to ANY rule, YOU MUST STOP and get explicit permission first. BREAKING THE LETTER OR SPIRIT OF THE RULES IS FAILURE.
## Foundational rules
- Doing it right is better than doing it fast. You are not in a rush. NEVER skip steps or take shortcuts.
- Tedious, systematic work is often the correct solution. Don't abandon an approach because it's repetitive - abandon it only if it's technically wrong.
- Honesty is a core value.
## Our relationship
- Act as a critical peer reviewer. Your job is to disagree with me when I'm wrong, not to please me. Prioritize accuracy and reasoning over agreement.
- YOU MUST speak up immediately when you don't know something or we're in over our heads
- YOU MUST call out bad ideas, unreasonable expectations, and mistakes - I depend on this
- NEVER be agreeable just to be nice - I NEED your HONEST technical judgment
- NEVER write the phrase "You're absolutely right!" You are not a sycophant. We're working together because I value your opinion. Do not agree with me unless you can justify it with evidence or reasoning.
- YOU MUST ALWAYS STOP and ask for clarification rather than making assumptions.
- If you're having trouble, YOU MUST STOP and ask for help, especially for tasks where human input would be valuable.
- When you disagree with my approach, YOU MUST push back. Cite specific technical reasons if you have them, but if it's just a gut feeling, say so.
- If you're uncomfortable pushing back out loud, just say "Houston, we have a problem". I'll know what you mean
- We discuss architectutral decisions (framework changes, major refactoring, system design) together before implementation. Routine fixes and clear implementations don't need discussion.
## Proactiveness
When asked to do something, just do it - including obvious follow-up actions needed to complete the task properly.
Only pause to ask for confirmation when:
- Multiple valid approaches exist and the choice matters
- The action would delete or significantly restructure existing code
- You genuinely don't understand what's being asked
- Your partner asked a question (answer the question, don't jump to implementation)
@.claude/docs/WORKFLOWS.md
@package.json
## Essential Commands
| Task | Command | Notes |
|-------------------|--------------------------|----------------------------------|
| **Development** | `./scripts/develop.sh` | ⚠️ Don't use manual build |
| **Build** | `make build` | Fat binaries (includes server) |
| **Build Slim** | `make build-slim` | Slim binaries |
| **Test** | `make test` | Full test suite |
| **Test Single** | `make test RUN=TestName` | Faster than full suite |
| **Test Postgres** | `make test-postgres` | Run tests with Postgres database |
| **Test Race** | `make test-race` | Run tests with Go race detector |
| **Lint** | `make lint` | Always run after changes |
| **Generate** | `make gen` | After database changes |
| **Format** | `make fmt` | Auto-format code |
| **Clean** | `make clean` | Clean build artifacts |
### Documentation Commands
- `pnpm run format-docs` - Format markdown tables in docs
- `pnpm run lint-docs` - Lint and fix markdown files
- `pnpm run storybook` - Run Storybook (from site directory)
## Critical Patterns
### Database Changes (ALWAYS FOLLOW)
1. Modify `coderd/database/queries/*.sql` files
2. Run `make gen`
3. If audit errors: update `enterprise/audit/table.go`
4. Run `make gen` again
### LSP Navigation (USE FIRST)
#### Go LSP (for backend code)
- **Find definitions**: `mcp__go-language-server__definition symbolName`
- **Find references**: `mcp__go-language-server__references symbolName`
- **Get type info**: `mcp__go-language-server__hover filePath line column`
- **Rename symbol**: `mcp__go-language-server__rename_symbol filePath line column newName`
#### TypeScript LSP (for frontend code in site/)
- **Find definitions**: `mcp__typescript-language-server__definition symbolName`
- **Find references**: `mcp__typescript-language-server__references symbolName`
- **Get type info**: `mcp__typescript-language-server__hover filePath line column`
- **Rename symbol**: `mcp__typescript-language-server__rename_symbol filePath line column newName`
### OAuth2 Error Handling
```go
// OAuth2-compliant error responses
writeOAuth2Error(ctx, rw, http.StatusBadRequest, "invalid_grant", "description")
```
### Authorization Context
```go
// Public endpoints needing system access
app, err := api.Database.GetOAuth2ProviderAppByClientID(dbauthz.AsSystemRestricted(ctx), clientID)
// Authenticated endpoints with user context
app, err := api.Database.GetOAuth2ProviderAppByClientID(ctx, clientID)
```
## Quick Reference
### Full workflows available in imported WORKFLOWS.md
### New Feature Checklist
- [ ] Run `git pull` to ensure latest code
- [ ] Check if feature touches database - you'll need migrations
- [ ] Check if feature touches audit logs - update `enterprise/audit/table.go`
## Architecture
- **coderd**: Main API service
- **provisionerd**: Infrastructure provisioning
- **Agents**: Workspace services (SSH, port forwarding)
- **Database**: PostgreSQL with `dbauthz` authorization
## Testing
### Race Condition Prevention
- Use unique identifiers: `fmt.Sprintf("test-client-%s-%d", t.Name(), time.Now().UnixNano())`
- Never use hardcoded names in concurrent tests
### OAuth2 Testing
- Full suite: `./scripts/oauth2/test-mcp-oauth2.sh`
- Manual testing: `./scripts/oauth2/test-manual-flow.sh`
### Timing Issues
NEVER use `time.Sleep` to mitigate timing issues. If an issue
seems like it should use `time.Sleep`, read through https://github.com/coder/quartz and specifically the [README](https://github.com/coder/quartz/blob/main/README.md) to better understand how to handle timing issues.
## Code Style
### Detailed guidelines in imported WORKFLOWS.md
- Follow [Uber Go Style Guide](https://github.com/uber-go/guide/blob/master/style.md)
- Commit format: `type(scope): message`
### Writing Comments
Code comments should be clear, well-formatted, and add meaningful context.
**Proper sentence structure**: Comments are sentences and should end with
periods or other appropriate punctuation. This improves readability and
maintains professional code standards.
**Explain why, not what**: Good comments explain the reasoning behind code
rather than describing what the code does. The code itself should be
self-documenting through clear naming and structure. Focus your comments on
non-obvious decisions, edge cases, or business logic that isn't immediately
apparent from reading the implementation.
**Line length and wrapping**: Keep comment lines to 80 characters wide
(including the comment prefix like `//` or `#`). When a comment spans multiple
lines, wrap it naturally at word boundaries rather than writing one sentence
per line. This creates more readable, paragraph-like blocks of documentation.
```go
// Good: Explains the rationale with proper sentence structure.
// We need a custom timeout here because workspace builds can take several
// minutes on slow networks, and the default 30s timeout causes false
// failures during initial template imports.
ctx, cancel := context.WithTimeout(ctx, 5*time.Minute)
// Bad: Describes what the code does without punctuation or wrapping
// Set a custom timeout
// Workspace builds can take a long time
// Default timeout is too short
ctx, cancel := context.WithTimeout(ctx, 5*time.Minute)
```
### Avoid Unnecessary Changes
When fixing a bug or adding a feature, don't modify code unrelated to your
task. Unnecessary changes make PRs harder to review and can introduce
regressions.
**Don't reword existing comments or code** unless the change is directly
motivated by your task. Rewording comments to be shorter or "cleaner" wastes
reviewer time and clutters the diff.
**Don't delete existing comments** that explain non-obvious behavior. These
comments preserve important context about why code works a certain way.
**When adding tests for new behavior**, add new test cases instead of modifying
existing ones. This preserves coverage for the original behavior and makes it
clear what the new test covers.
## Detailed Development Guides
@.claude/docs/ARCHITECTURE.md
@.claude/docs/OAUTH2.md
@.claude/docs/TESTING.md
@.claude/docs/TROUBLESHOOTING.md
@.claude/docs/DATABASE.md
## Local Configuration
These files may be gitignored, read manually if not auto-loaded.
@AGENTS.local.md
## Common Pitfalls
1. **Audit table errors** → Update `enterprise/audit/table.go`
2. **OAuth2 errors** → Return RFC-compliant format
3. **Race conditions** → Use unique test identifiers
4. **Missing newlines** → Ensure files end with newline
---
*This file stays lean and actionable. Detailed workflows and explanations are imported automatically.*
-31
View File
@@ -1,31 +0,0 @@
# These APIs are versioned, so any changes need to be carefully reviewed for
# whether to bump API major or minor versions.
agent/proto/ @spikecurtis @johnstcn
provisionerd/proto/ @spikecurtis @johnstcn
provisionersdk/proto/ @spikecurtis @johnstcn
tailnet/proto/ @spikecurtis @johnstcn
vpn/vpn.proto @spikecurtis @johnstcn
vpn/version.go @spikecurtis @johnstcn
# This caching code is particularly tricky, and one must be very careful when
# altering it.
coderd/files/ @aslilac
coderd/dynamicparameters/ @Emyrk
coderd/rbac/ @Emyrk
# Mainly dependent on coder/guts, which is maintained by @Emyrk
scripts/apitypings/ @Emyrk
scripts/gensite/ @aslilac
# The blood and guts of the autostop algorithm, which is quite complex and
# requires elite ball knowledge of most of the scheduling code to make changes
# without inadvertently affecting other parts of the codebase.
coderd/schedule/autostop.go @deansheather @DanielleMaywood
# Usage tracking code requires intimate knowledge of Tallyman and Metronome, as
# well as guidance from revenue.
coderd/usage/ @deansheather @spikecurtis
enterprise/coderd/usage/ @deansheather @spikecurtis
.github/ @jdomeracki-coder
-2
View File
@@ -1,2 +0,0 @@
<!-- markdownlint-disable MD041 -->
[https://coder.com/docs/about/contributing/CODE_OF_CONDUCT](https://coder.com/docs/about/contributing/CODE_OF_CONDUCT)
-2
View File
@@ -1,2 +0,0 @@
<!-- markdownlint-disable MD041 -->
[https://coder.com/docs/CONTRIBUTING](https://coder.com/docs/CONTRIBUTING)
-661
View File
@@ -1,661 +0,0 @@
GNU AFFERO GENERAL PUBLIC LICENSE
Version 3, 19 November 2007
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU Affero General Public License is a free, copyleft license for
software and other kinds of works, specifically designed to ensure
cooperation with the community in the case of network server software.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
our General Public Licenses are intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
Developers that use our General Public Licenses protect your rights
with two steps: (1) assert copyright on the software, and (2) offer
you this License which gives you legal permission to copy, distribute
and/or modify the software.
A secondary benefit of defending all users' freedom is that
improvements made in alternate versions of the program, if they
receive widespread use, become available for other developers to
incorporate. Many developers of free software are heartened and
encouraged by the resulting cooperation. However, in the case of
software used on network servers, this result may fail to come about.
The GNU General Public License permits making a modified version and
letting the public access it on a server without ever releasing its
source code to the public.
The GNU Affero General Public License is designed specifically to
ensure that, in such cases, the modified source code becomes available
to the community. It requires the operator of a network server to
provide the source code of the modified version running there to the
users of that server. Therefore, public use of a modified version, on
a publicly accessible server, gives the public access to the source
code of the modified version.
An older license, called the Affero General Public License and
published by Affero, was designed to accomplish similar goals. This is
a different license, not a version of the Affero GPL, but Affero has
released a new version of the Affero GPL which permits relicensing under
this license.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU Affero General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Remote Network Interaction; Use with the GNU General Public License.
Notwithstanding any other provision of this License, if you modify the
Program, your modified version must prominently offer all users
interacting with it remotely through a computer network (if your version
supports such interaction) an opportunity to receive the Corresponding
Source of your version by providing access to the Corresponding Source
from a network server at no charge, through some standard or customary
means of facilitating copying of software. This Corresponding Source
shall include the Corresponding Source for any work covered by version 3
of the GNU General Public License that is incorporated pursuant to the
following paragraph.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the work with which it is combined will remain governed by version
3 of the GNU General Public License.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU Affero General Public License from time to time. Such new versions
will be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU Affero General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU Affero General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU Affero General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If your software can interact with users remotely through a computer
network, you should also make sure that it provides a way for users to
get its source. For example, if your program is a web application, its
interface could display a "Source" link that leads users to an archive
of the code. There are many ways you could offer source, and different
solutions will be better for different programs; see section 13 for the
specific requirements.
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU AGPL, see
<https://www.gnu.org/licenses/>.
-31
View File
@@ -1,31 +0,0 @@
## Acceptance
By using any software and associated documentation files under Coder
Technologies Inc.s ("Coder") directory named "enterprise" ("Enterprise
Software"), you agree to all of the terms and conditions below.
## Copyright License
The licensor grants you a non-exclusive, royalty-free, worldwide,
non-sublicensable, non-transferable license to use, copy, distribute, make
available, modify and prepare derivative works of the Enterprise Software, in
each case subject to the limitations and conditions below.
## Limitations
You may not move, change, disable, or circumvent the license key functionality
in the software, and you may not remove or obscure any functionality in the
software that is protected by the license key.
You may not alter, remove, or obscure any licensing, copyright, or other notices
of the licensor in the software.
You agree that Coder and/or its licensors (as applicable) retain all right,
title and interest in and to all such modifications and/or patches.
## Additional Terms
This Enterprise Software may only be used in production, if you (and any entity
that you represent) have agreed to, and are in compliance with, the Coders
Terms of Service, available at https://coder.com/legal/terms-of-service, or
other agreement governing the use of the Software, as agreed by you and Coder.
+64 -1165
View File
File diff suppressed because it is too large Load Diff
+47 -102
View File
@@ -1,133 +1,78 @@
<!-- markdownlint-disable MD041 -->
<div align="center">
<a href="https://coder.com#gh-light-mode-only">
<img src="./docs/images/logo-black.png" alt="Coder Logo Light" style="width: 128px">
</a>
<a href="https://coder.com#gh-dark-mode-only">
<img src="./docs/images/logo-white.png" alt="Coder Logo Dark" style="width: 128px">
</a>
[![coder](https://github.com/coder/coder/actions/workflows/coder.yaml/badge.svg)](https://github.com/coder/coder/actions/workflows/coder.yaml)
[![codecov](https://codecov.io/gh/coder/coder/branch/main/graph/badge.svg?token=TNLW3OAP6G)](https://codecov.io/gh/coder/coder)
<h1>
Self-Hosted Cloud Development Environments
</h1>
# Coder v2
<a href="https://coder.com#gh-light-mode-only">
<img src="./docs/images/banner-black.png" alt="Coder Banner Light" style="width: 650px">
</a>
<a href="https://coder.com#gh-dark-mode-only">
<img src="./docs/images/banner-white.png" alt="Coder Banner Dark" style="width: 650px">
</a>
This repository contains source code for Coder V2. Additional documentation:
<br>
<br>
- [Workspaces V2 RFC](https://www.notion.so/coderhq/b48040da8bfe46eca1f32749b69420dd?v=a4e7d23495094644b939b08caba8e381&p=e908a8cd54804ddd910367abf03c8d0a)
[Quickstart](#quickstart) | [Docs](https://coder.com/docs) | [Why Coder](https://coder.com/why) | [Premium](https://coder.com/pricing#compare-plans)
## Directory Structure
[![discord](https://img.shields.io/discord/747933592273027093?label=discord)](https://discord.gg/coder)
[![release](https://img.shields.io/github/v/release/coder/coder)](https://github.com/coder/coder/releases/latest)
[![godoc](https://pkg.go.dev/badge/github.com/coder/coder.svg)](https://pkg.go.dev/github.com/coder/coder)
[![Go Report Card](https://goreportcard.com/badge/github.com/coder/coder/v2)](https://goreportcard.com/report/github.com/coder/coder/v2)
[![OpenSSF Best Practices](https://www.bestpractices.dev/projects/9511/badge)](https://www.bestpractices.dev/projects/9511)
[![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/coder/coder/badge)](https://scorecard.dev/viewer/?uri=github.com%2Fcoder%2Fcoder)
[![license](https://img.shields.io/github/license/coder/coder)](./LICENSE)
- `.github/`: Settings for [Dependabot for updating dependencies](https://docs.github.com/en/code-security/supply-chain-security/customizing-dependency-updates) and [build/deploy pipelines with GitHub Actions](https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions).
- [`semantic.yaml`](./github/semantic.yaml): Configuration for [semantic pull requests](https://github.com/apps/semantic-pull-requests)
- `examples`: Example terraform project templates.
- `site`: Front-end UI code.
</div>
## Development
[Coder](https://coder.com) enables organizations to set up development environments in their public or private cloud infrastructure. Cloud development environments are defined with Terraform, connected through a secure high-speed Wireguard® tunnel, and automatically shut down when not used to save on costs. Coder gives engineering teams the flexibility to use the cloud for workloads most beneficial to them.
### Pre-requisites
- Define cloud development environments in Terraform
- EC2 VMs, Kubernetes Pods, Docker Containers, etc.
- Automatically shutdown idle resources to save on costs
- Onboard developers in seconds instead of days
- `git`
- `go` version 1.17, with the `GOPATH` environment variable set
- `node`
- `yarn`
<p align="center">
<img src="./docs/images/hero-image.png" alt="Coder Hero Image">
</p>
### Cloning
## Quickstart
- `git clone https://github.com/coder/coder`
- `cd coder`
The most convenient way to try Coder is to install it on your local machine and experiment with provisioning cloud development environments using Docker (works on Linux, macOS, and Windows).
### Building
```shell
# First, install Coder
curl -L https://coder.com/install.sh | sh
- `make build`
- `make install`
# Start the Coder server (caches data in ~/.cache/coder)
coder server
The `coder` CLI binary will now be available at `$GOPATH/bin/coder`
# Navigate to http://localhost:3000 to create your initial user,
# create a Docker template and provision a workspace
```
### Running
## Install
After building, the binaries will be available at:
- `dist/coder_{os}_{arch}/coder`
The easiest way to install Coder is to use our
[install script](https://github.com/coder/coder/blob/main/install.sh) for Linux
and macOS. For Windows, use the latest `..._installer.exe` file from GitHub
Releases.
For the purpose of these steps, an OS of `linux` and an arch of `amd64` is assumed.
```shell
curl -L https://coder.com/install.sh | sh
```
To manually run the server and go through first-time set up, run the following commands in separate terminals:
- `dist/coder_linux_amd64/coder daemon` <-- starts the Coder server on port 3000
- `dist/coder_linux_amd64/coder login http://localhost:3000` <-- runs through first-time setup, creating a user and org
You can run the install script with `--dry-run` to see the commands that will be used to install without executing them. Run the install script with `--help` for additional flags.
You'll now be able to login and access the server.
> See [install](https://coder.com/docs/install) for additional methods.
To create a project, run:
- `dist/coder_linux_amd64/coder projects create -d /path/to/project`
Once installed, you can start a production deployment with a single command:
### Development
```shell
# Automatically sets up an external access URL on *.try.coder.app
coder server
- `./develop.sh`
# Requires a PostgreSQL instance (version 13 or higher) and external access URL
coder server --postgres-url <url> --access-url <url>
```
The `develop.sh` script does three things:
Use `coder --help` to get a list of flags and environment variables. Use our [install guides](https://coder.com/docs/install) for a complete walkthrough.
- runs `coder daemon` locally on port `3000`
- runs `webpack-dev-server` on port `8080`
- sets up an initial user and organization
## Documentation
This is the recommend flow for working on the front-end, as hot-reload is set up as part of the webpack config.
Browse our docs [here](https://coder.com/docs) or visit a specific section below:
## Front-End Plan
- [**Templates**](https://coder.com/docs/templates): Templates are written in Terraform and describe the infrastructure for workspaces
- [**Workspaces**](https://coder.com/docs/workspaces): Workspaces contain the IDEs, dependencies, and configuration information needed for software development
- [**IDEs**](https://coder.com/docs/ides): Connect your existing editor to a workspace
- [**Administration**](https://coder.com/docs/admin): Learn how to operate Coder
- [**Premium**](https://coder.com/pricing#compare-plans): Learn about our paid features built for large teams
For the front-end team, we're planning on 2 phases to the 'v2' work:
## Support
### Phase 1
Feel free to [open an issue](https://github.com/coder/coder/issues/new) if you have questions, run into bugs, or have a feature request.
Phase 1 is the 'new-wine-in-an-old-bottle' approach - we want to preserve the look and feel (UX) of v1, while testing and validating the market fit of our new v2 provisioner model. This means that we'll preserve Material UI and re-use components from v1 (porting them over to the v2 codebase).
[Join our Discord](https://discord.gg/coder) to provide feedback on in-progress features and chat with the community using Coder!
### Phase 2
## Integrations
Phase 2 is the 'new-wine-in-a-new-bottle' - which we can do once we've successfully packaged the new wine in the old bottle.
We are always working on new integrations. Please feel free to open an issue and ask for an integration. Contributions are welcome in any official or community repositories.
### Official
- [**VS Code Extension**](https://marketplace.visualstudio.com/items?itemName=coder.coder-remote): Open any Coder workspace in VS Code with a single click
- [**JetBrains Toolbox Plugin**](https://plugins.jetbrains.com/plugin/26968-coder): Open any Coder workspace from JetBrains Toolbox with a single click
- [**JetBrains Gateway Plugin**](https://plugins.jetbrains.com/plugin/19620-coder): Open any Coder workspace in JetBrains Gateway with a single click
- [**Dev Container Builder**](https://github.com/coder/envbuilder): Build development environments using `devcontainer.json` on Docker, Kubernetes, and OpenShift
- [**Coder Registry**](https://registry.coder.com): Build and extend development environments with common use-cases
- [**Kubernetes Log Stream**](https://github.com/coder/coder-logstream-kube): Stream Kubernetes Pod events to the Coder startup logs
- [**Self-Hosted VS Code Extension Marketplace**](https://github.com/coder/code-marketplace): A private extension marketplace that works in restricted or airgapped networks integrating with [code-server](https://github.com/coder/code-server).
- [**Setup Coder**](https://github.com/marketplace/actions/setup-coder): An action to setup coder CLI in GitHub workflows.
### Community
- [**Provision Coder with Terraform**](https://github.com/ElliotG/coder-oss-tf): Provision Coder on Google GKE, Azure AKS, AWS EKS, DigitalOcean DOKS, IBMCloud K8s, OVHCloud K8s, and Scaleway K8s Kapsule with Terraform
- [**Coder Template GitHub Action**](https://github.com/marketplace/actions/update-coder-template): A GitHub Action that updates Coder templates
## Contributing
We are always happy to see new contributors to Coder. If you are new to the Coder codebase, we have
[a guide on how to get started](https://coder.com/docs/CONTRIBUTING). We'd love to see your
contributions!
## Hiring
Apply [here](https://jobs.ashbyhq.com/coder?utm_source=github&utm_medium=readme&utm_campaign=unknown) if you're interested in joining our team.
In other words, once we've validated that the new strategy fits and is desirable for our customers, we'd like to build a new, v2-native UI (leveraging designers on the team to build a first-class experience around the new provisioner model).
-81
View File
@@ -1,81 +0,0 @@
# Coder Security
Coder welcomes feedback from security researchers and the general public to help
improve our security. If you believe you have discovered a vulnerability,
privacy issue, exposed data, or other security issues in any of our assets, we
want to hear from you. This policy outlines steps for reporting vulnerabilities
to us, what we expect, what you can expect from us.
You can see the pretty version [here](https://coder.com/security/policy)
## Why Coder's security matters
If an attacker could fully compromise a Coder installation, they could spin up
expensive workstations, steal valuable credentials, or steal proprietary source
code. We take this risk very seriously and employ routine pen testing,
vulnerability scanning, and code reviews. We also welcome the contributions from
the community that helped make this product possible.
## Where should I report security issues?
Please report security issues to <security@coder.com>, providing all relevant
information. The more details you provide, the easier it will be for us to
triage and fix the issue.
## Out of Scope
Our primary concern is around an abuse of the Coder application that allows an
attacker to gain access to another users workspace, or spin up unwanted
workspaces.
- DOS/DDOS attacks affecting availability --> While we do support rate limiting
of requests, we primarily leave this to the owner of the Coder installation.
Our rationale is that a DOS attack only affecting availability is not a
valuable target for attackers.
- Abuse of a compromised user credential --> If a user credential is compromised
outside of the Coder ecosystem, then we consider it beyond the scope of our
application. However, if an unprivileged user could escalate their permissions
or gain access to another workspace, that is a cause for concern.
- Vulnerabilities in third party systems --> Vulnerabilities discovered in
out-of-scope systems should be reported to the appropriate vendor or
applicable authority.
## Our Commitments
When working with us, according to this policy, you can expect us to:
- Respond to your report promptly, and work with you to understand and validate
your report;
- Strive to keep you informed about the progress of a vulnerability as it is
processed;
- Work to remediate discovered vulnerabilities in a timely manner, within our
operational constraints; and
- Extend Safe Harbor for your vulnerability research that is related to this
policy.
## Our Expectations
In participating in our vulnerability disclosure program in good faith, we ask
that you:
- Play by the rules, including following this policy and any other relevant
agreements. If there is any inconsistency between this policy and any other
applicable terms, the terms of this policy will prevail;
- Report any vulnerability youve discovered promptly;
- Avoid violating the privacy of others, disrupting our systems, destroying
data, and/or harming user experience;
- Use only the Official Channels to discuss vulnerability information with us;
- Provide us a reasonable amount of time (at least 90 days from the initial
report) to resolve the issue before you disclose it publicly;
- Perform testing only on in-scope systems, and respect systems and activities
which are out-of-scope;
- If a vulnerability provides unintended access to data: Limit the amount of
data you access to the minimum required for effectively demonstrating a Proof
of Concept; and cease testing and submit a report immediately if you encounter
any user data during testing, such as Personally Identifiable Information
(PII), Personal Healthcare Information (PHI), credit card data, or proprietary
information;
- You should only interact with test accounts you own or with explicit
permission from
- the account holder; and
- Do not engage in extortion.
+284 -2217
View File
File diff suppressed because it is too large Load Diff
-45
View File
@@ -1,45 +0,0 @@
package agent
import (
"testing"
"github.com/google/uuid"
"github.com/stretchr/testify/require"
"cdr.dev/slog"
"cdr.dev/slog/sloggers/slogtest"
"github.com/coder/coder/v2/agent/proto"
"github.com/coder/coder/v2/testutil"
)
// TestReportConnectionEmpty tests that reportConnection() doesn't choke if given an empty IP string, which is what we
// send if we cannot get the remote address.
func TestReportConnectionEmpty(t *testing.T) {
t.Parallel()
connID := uuid.UUID{1}
logger := slogtest.Make(t, &slogtest.Options{IgnoreErrors: true}).Leveled(slog.LevelDebug)
ctx := testutil.Context(t, testutil.WaitShort)
uut := &agent{
hardCtx: ctx,
logger: logger,
}
disconnected := uut.reportConnection(connID, proto.Connection_TYPE_UNSPECIFIED, "")
require.Len(t, uut.reportConnections, 1)
req0 := uut.reportConnections[0]
require.Equal(t, proto.Connection_TYPE_UNSPECIFIED, req0.GetConnection().GetType())
require.Equal(t, "", req0.GetConnection().Ip)
require.Equal(t, connID[:], req0.GetConnection().GetId())
require.Equal(t, proto.Connection_CONNECT, req0.GetConnection().GetAction())
disconnected(0, "because")
require.Len(t, uut.reportConnections, 2)
req1 := uut.reportConnections[1]
require.Equal(t, proto.Connection_TYPE_UNSPECIFIED, req1.GetConnection().GetType())
require.Equal(t, "", req1.GetConnection().Ip)
require.Equal(t, connID[:], req1.GetConnection().GetId())
require.Equal(t, proto.Connection_DISCONNECT, req1.GetConnection().GetAction())
require.Equal(t, "because", req1.GetConnection().GetReason())
}
+62 -3645
View File
File diff suppressed because it is too large Load Diff
-190
View File
@@ -1,190 +0,0 @@
// Code generated by MockGen. DO NOT EDIT.
// Source: .. (interfaces: ContainerCLI,DevcontainerCLI)
//
// Generated by this command:
//
// mockgen -destination ./acmock.go -package acmock .. ContainerCLI,DevcontainerCLI
//
// Package acmock is a generated GoMock package.
package acmock
import (
context "context"
reflect "reflect"
agentcontainers "github.com/coder/coder/v2/agent/agentcontainers"
codersdk "github.com/coder/coder/v2/codersdk"
gomock "go.uber.org/mock/gomock"
)
// MockContainerCLI is a mock of ContainerCLI interface.
type MockContainerCLI struct {
ctrl *gomock.Controller
recorder *MockContainerCLIMockRecorder
isgomock struct{}
}
// MockContainerCLIMockRecorder is the mock recorder for MockContainerCLI.
type MockContainerCLIMockRecorder struct {
mock *MockContainerCLI
}
// NewMockContainerCLI creates a new mock instance.
func NewMockContainerCLI(ctrl *gomock.Controller) *MockContainerCLI {
mock := &MockContainerCLI{ctrl: ctrl}
mock.recorder = &MockContainerCLIMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockContainerCLI) EXPECT() *MockContainerCLIMockRecorder {
return m.recorder
}
// Copy mocks base method.
func (m *MockContainerCLI) Copy(ctx context.Context, containerName, src, dst string) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Copy", ctx, containerName, src, dst)
ret0, _ := ret[0].(error)
return ret0
}
// Copy indicates an expected call of Copy.
func (mr *MockContainerCLIMockRecorder) Copy(ctx, containerName, src, dst any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Copy", reflect.TypeOf((*MockContainerCLI)(nil).Copy), ctx, containerName, src, dst)
}
// DetectArchitecture mocks base method.
func (m *MockContainerCLI) DetectArchitecture(ctx context.Context, containerName string) (string, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "DetectArchitecture", ctx, containerName)
ret0, _ := ret[0].(string)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// DetectArchitecture indicates an expected call of DetectArchitecture.
func (mr *MockContainerCLIMockRecorder) DetectArchitecture(ctx, containerName any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DetectArchitecture", reflect.TypeOf((*MockContainerCLI)(nil).DetectArchitecture), ctx, containerName)
}
// ExecAs mocks base method.
func (m *MockContainerCLI) ExecAs(ctx context.Context, containerName, user string, args ...string) ([]byte, error) {
m.ctrl.T.Helper()
varargs := []any{ctx, containerName, user}
for _, a := range args {
varargs = append(varargs, a)
}
ret := m.ctrl.Call(m, "ExecAs", varargs...)
ret0, _ := ret[0].([]byte)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// ExecAs indicates an expected call of ExecAs.
func (mr *MockContainerCLIMockRecorder) ExecAs(ctx, containerName, user any, args ...any) *gomock.Call {
mr.mock.ctrl.T.Helper()
varargs := append([]any{ctx, containerName, user}, args...)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ExecAs", reflect.TypeOf((*MockContainerCLI)(nil).ExecAs), varargs...)
}
// List mocks base method.
func (m *MockContainerCLI) List(ctx context.Context) (codersdk.WorkspaceAgentListContainersResponse, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "List", ctx)
ret0, _ := ret[0].(codersdk.WorkspaceAgentListContainersResponse)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// List indicates an expected call of List.
func (mr *MockContainerCLIMockRecorder) List(ctx any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*MockContainerCLI)(nil).List), ctx)
}
// MockDevcontainerCLI is a mock of DevcontainerCLI interface.
type MockDevcontainerCLI struct {
ctrl *gomock.Controller
recorder *MockDevcontainerCLIMockRecorder
isgomock struct{}
}
// MockDevcontainerCLIMockRecorder is the mock recorder for MockDevcontainerCLI.
type MockDevcontainerCLIMockRecorder struct {
mock *MockDevcontainerCLI
}
// NewMockDevcontainerCLI creates a new mock instance.
func NewMockDevcontainerCLI(ctrl *gomock.Controller) *MockDevcontainerCLI {
mock := &MockDevcontainerCLI{ctrl: ctrl}
mock.recorder = &MockDevcontainerCLIMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockDevcontainerCLI) EXPECT() *MockDevcontainerCLIMockRecorder {
return m.recorder
}
// Exec mocks base method.
func (m *MockDevcontainerCLI) Exec(ctx context.Context, workspaceFolder, configPath, cmd string, cmdArgs []string, opts ...agentcontainers.DevcontainerCLIExecOptions) error {
m.ctrl.T.Helper()
varargs := []any{ctx, workspaceFolder, configPath, cmd, cmdArgs}
for _, a := range opts {
varargs = append(varargs, a)
}
ret := m.ctrl.Call(m, "Exec", varargs...)
ret0, _ := ret[0].(error)
return ret0
}
// Exec indicates an expected call of Exec.
func (mr *MockDevcontainerCLIMockRecorder) Exec(ctx, workspaceFolder, configPath, cmd, cmdArgs any, opts ...any) *gomock.Call {
mr.mock.ctrl.T.Helper()
varargs := append([]any{ctx, workspaceFolder, configPath, cmd, cmdArgs}, opts...)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Exec", reflect.TypeOf((*MockDevcontainerCLI)(nil).Exec), varargs...)
}
// ReadConfig mocks base method.
func (m *MockDevcontainerCLI) ReadConfig(ctx context.Context, workspaceFolder, configPath string, env []string, opts ...agentcontainers.DevcontainerCLIReadConfigOptions) (agentcontainers.DevcontainerConfig, error) {
m.ctrl.T.Helper()
varargs := []any{ctx, workspaceFolder, configPath, env}
for _, a := range opts {
varargs = append(varargs, a)
}
ret := m.ctrl.Call(m, "ReadConfig", varargs...)
ret0, _ := ret[0].(agentcontainers.DevcontainerConfig)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// ReadConfig indicates an expected call of ReadConfig.
func (mr *MockDevcontainerCLIMockRecorder) ReadConfig(ctx, workspaceFolder, configPath, env any, opts ...any) *gomock.Call {
mr.mock.ctrl.T.Helper()
varargs := append([]any{ctx, workspaceFolder, configPath, env}, opts...)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReadConfig", reflect.TypeOf((*MockDevcontainerCLI)(nil).ReadConfig), varargs...)
}
// Up mocks base method.
func (m *MockDevcontainerCLI) Up(ctx context.Context, workspaceFolder, configPath string, opts ...agentcontainers.DevcontainerCLIUpOptions) (string, error) {
m.ctrl.T.Helper()
varargs := []any{ctx, workspaceFolder, configPath}
for _, a := range opts {
varargs = append(varargs, a)
}
ret := m.ctrl.Call(m, "Up", varargs...)
ret0, _ := ret[0].(string)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// Up indicates an expected call of Up.
func (mr *MockDevcontainerCLIMockRecorder) Up(ctx, workspaceFolder, configPath any, opts ...any) *gomock.Call {
mr.mock.ctrl.T.Helper()
varargs := append([]any{ctx, workspaceFolder, configPath}, opts...)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Up", reflect.TypeOf((*MockDevcontainerCLI)(nil).Up), varargs...)
}
-4
View File
@@ -1,4 +0,0 @@
// Package acmock contains a mock implementation of agentcontainers.Lister for use in tests.
package acmock
//go:generate mockgen -destination ./acmock.go -package acmock .. ContainerCLI,DevcontainerCLI

Some files were not shown because too many files have changed in this diff Show More