Compare commits
41 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| fe925a39e9 | |||
| 519043890b | |||
| 8146d002e9 | |||
| 2cc902f525 | |||
| fc8c950529 | |||
| def36fa93a | |||
| c8ae865eed | |||
| 53d489ec19 | |||
| 8bc5c1afdf | |||
| 749d1ac64b | |||
| df885f57d5 | |||
| 82bceec144 | |||
| 52e4d994aa | |||
| 5677f22185 | |||
| 1005371a99 | |||
| c7dfb519a6 | |||
| 1402a08523 | |||
| 369c4a0019 | |||
| b4c2aed001 | |||
| 6ac95bfca6 | |||
| 6e53335ce7 | |||
| 55a0160c2c | |||
| 8165782d9c | |||
| e79f9b05da | |||
| 72bc74cb0a | |||
| f895d1f11f | |||
| 2b9a849f05 | |||
| fdc17eee0d | |||
| 1c9e124d6c | |||
| 926e59ea9d | |||
| cfffbe5c65 | |||
| 8d24602cf5 | |||
| 21c3d1cf9e | |||
| 6b4c8b5817 | |||
| 99c8eb180d | |||
| c01b8f7d65 | |||
| b23a58c8fe | |||
| 11e1d99625 | |||
| 8cee1fbdba | |||
| 688ab17030 | |||
| 94e50698e7 |
@@ -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.
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
```
|
||||
@@ -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.)
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -1,15 +0,0 @@
|
||||
{
|
||||
"hooks": {
|
||||
"PostToolUse": [
|
||||
{
|
||||
"matcher": "Edit|Write|MultiEdit",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": ".claude/scripts/format.sh"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
AGENTS.md
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
@@ -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
|
||||
@@ -1,4 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Start Docker service if not already running.
|
||||
sudo service docker start
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -0,0 +1 @@
|
||||
site @coder/coder-frontend
|
||||
@@ -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"
|
||||
@@ -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"
|
||||
@@ -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"
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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 }}
|
||||
@@ -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
|
||||
@@ -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 }}
|
||||
@@ -1,2 +0,0 @@
|
||||
enabled: true
|
||||
preservePullRequestTitle: true
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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}"
|
||||
@@ -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}
|
||||
@@ -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"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
@@ -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.
|
||||
|
||||
-->
|
||||
@@ -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
|
||||
@@ -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
@@ -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
|
||||
@@ -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(" ")
|
||||
}
|
||||
@@ -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 }}
|
||||
@@ -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 }}
|
||||
@@ -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}"
|
||||
@@ -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
|
||||
@@ -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 }}
|
||||
@@ -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
|
||||
@@ -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 }}
|
||||
@@ -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
|
||||
@@ -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 }}
|
||||
@@ -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
|
||||
@@ -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
|
||||
+13
-985
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
@@ -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 }}"
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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 actor’s 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}"
|
||||
@@ -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/**",
|
||||
]
|
||||
@@ -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 }}
|
||||
@@ -1,4 +0,0 @@
|
||||
rules:
|
||||
cache-poisoning:
|
||||
ignore:
|
||||
- "ci.yaml:184"
|
||||
+21
-86
@@ -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
@@ -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
@@ -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
|
||||
@@ -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]
|
||||
@@ -1,3 +0,0 @@
|
||||
{
|
||||
"ignores": ["PLAN.md"],
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
Vendored
+10
-14
@@ -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"
|
||||
]
|
||||
}
|
||||
|
||||
Vendored
-45
@@ -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": "$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"
|
||||
}
|
||||
}
|
||||
Vendored
+84
-64
@@ -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,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
@@ -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
|
||||
@@ -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)
|
||||
@@ -1,2 +0,0 @@
|
||||
<!-- markdownlint-disable MD041 -->
|
||||
[https://coder.com/docs/CONTRIBUTING](https://coder.com/docs/CONTRIBUTING)
|
||||
@@ -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/>.
|
||||
@@ -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 Coder’s
|
||||
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.
|
||||
@@ -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>
|
||||
[](https://github.com/coder/coder/actions/workflows/coder.yaml)
|
||||
[](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
|
||||
|
||||
[](https://discord.gg/coder)
|
||||
[](https://github.com/coder/coder/releases/latest)
|
||||
[](https://pkg.go.dev/github.com/coder/coder)
|
||||
[](https://goreportcard.com/report/github.com/coder/coder/v2)
|
||||
[](https://www.bestpractices.dev/projects/9511)
|
||||
[](https://scorecard.dev/viewer/?uri=github.com%2Fcoder%2Fcoder)
|
||||
[](./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
@@ -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 you’ve 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
File diff suppressed because it is too large
Load Diff
@@ -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
File diff suppressed because it is too large
Load Diff
@@ -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...)
|
||||
}
|
||||
@@ -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
Reference in New Issue
Block a user