Skip to main content

Contributing

Overview

Panduan untuk berkontribusi ke Centralize Management System.

Branch Strategy

main          ─────────────────────────────────▶ Production

├── develop ─────────────────────────────────▶ Development
│ │
│ ├── feature/xxx ──────────────────────▶ Feature branch
│ │
│ └── bugfix/xxx ───────────────────────▶ Bugfix branch

└── release/x.x.x ──────────────────────────▶ Release branch

Branch Naming

TypePatternExample
Featurefeature/descriptionfeature/add-alerting
Bugfixbugfix/descriptionbugfix/fix-login
Hotfixhotfix/descriptionhotfix/security-patch
Releaserelease/x.x.xrelease/1.0.0

Commit Messages

Format

<type>(<scope>): <subject>

<body>

<footer>

Types

TypeDescription
featNew feature
fixBug fix
docsDocumentation
styleFormatting
refactorCode refactoring
testTests
choreMaintenance

Examples

feat(alerting): add Telegram notification

- Add Telegram bot integration
- Add verification code flow
- Add broadcast webhook

Closes #123
fix(backend): fix JWT token validation

- Fix JWKS caching issue
- Add token expiration check

Fixes #456

Pull Request Process

1. Create Feature Branch

git checkout -b feature/my-feature develop

2. Make Changes

# Make changes
git add .
git commit -m "feat(scope): description"

3. Push to Remote

git push origin feature/my-feature

4. Create Pull Request

  • Title: <type>(<scope>): <description>
  • Description: What, Why, How
  • Link to issue

5. Code Review

  • At least 1 approval required
  • All CI checks must pass
  • No merge conflicts

6. Merge

git checkout develop
git merge --no-ff feature/my-feature
git push origin develop

Code Standards

Go Backend

// Good
func HandleLogin(w http.ResponseWriter, r *http.Request) {
var req loginRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
writeError(w, http.StatusBadRequest, "invalid json body")
return
}
// ...
}

// Bad
func handlelogin(w http.ResponseWriter, r *http.Request) {
var req loginRequest
json.NewDecoder(r.Body).Decode(&req)
// ...
}

TypeScript Frontend

// Good
interface LogEntry {
id: string;
message: string;
level: string;
timestamp: string;
}

function LogItem({ log }: { log: LogEntry }) {
return <div>{log.message}</div>;
}

// Bad
function logItem(props: any) {
return <div>{props.log.message}</div>
}

Testing

Backend Tests

func TestHandleLogin(t *testing.T) {
// Arrange
req := loginRequest{
Username: "admin",
Password: "Admin123!",
}

// Act
resp, err := login(req)

// Assert
if err != nil {
t.Errorf("unexpected error: %v", err)
}
if resp.Token.AccessToken == "" {
t.Error("expected access token")
}
}

Frontend Tests

describe('LogItem', () => {
it('renders message', () => {
const log = { id: '1', message: 'Test log', level: 'info' };
render(<LogItem log={log} />);
expect(screen.getByText('Test log')).toBeInTheDocument();
});
});

Documentation

  • Update README if needed
  • Add/update API documentation
  • Add/update inline comments
  • Update CHANGELOG

Code Review Checklist

  • Code follows style guidelines
  • Tests are included
  • Documentation is updated
  • No breaking changes
  • Security considerations
  • Performance implications

Next Steps