summaryrefslogtreecommitdiff
path: root/archived/projt-launcher/ci/github-script/commits.js
diff options
context:
space:
mode:
authorMehmet Samet Duman <yongdohyun@projecttick.org>2026-04-02 18:51:45 +0300
committerMehmet Samet Duman <yongdohyun@projecttick.org>2026-04-02 18:51:45 +0300
commitd3261e64152397db2dca4d691a990c6bc2a6f4dd (patch)
treefac2f7be638651181a72453d714f0f96675c2b8b /archived/projt-launcher/ci/github-script/commits.js
parent31b9a8949ed0a288143e23bf739f2eb64fdc63be (diff)
downloadProject-Tick-d3261e64152397db2dca4d691a990c6bc2a6f4dd.tar.gz
Project-Tick-d3261e64152397db2dca4d691a990c6bc2a6f4dd.zip
NOISSUE add archived projects
Signed-off-by: Mehmet Samet Duman <yongdohyun@projecttick.org>
Diffstat (limited to 'archived/projt-launcher/ci/github-script/commits.js')
-rw-r--r--archived/projt-launcher/ci/github-script/commits.js336
1 files changed, 336 insertions, 0 deletions
diff --git a/archived/projt-launcher/ci/github-script/commits.js b/archived/projt-launcher/ci/github-script/commits.js
new file mode 100644
index 0000000000..27dbd61eb6
--- /dev/null
+++ b/archived/projt-launcher/ci/github-script/commits.js
@@ -0,0 +1,336 @@
+/**
+ * ProjT Launcher - Commit Validation for Pull Requests
+ * Validates commit messages, structure, and conventions
+ */
+
+const { classify } = require('../supportedBranches.js')
+const withRateLimit = require('./withRateLimit.js')
+const { dismissReviews, postReview } = require('./reviews.js')
+
+const commitTypeConfig = (() => {
+ try {
+ return require('./commit-types.json')
+ } catch (error) {
+ console.warn(`commit validator: could not load commit-types.json (${error.message})`)
+ return {}
+ }
+})()
+
+const parseCommitTypeList = (value) => {
+ if (!value) {
+ return []
+ }
+ return value
+ .split(',')
+ .map((entry) => entry.trim().toLowerCase())
+ .filter(Boolean)
+}
+
+const DEFAULT_COMMIT_TYPES = [
+ 'build',
+ 'chore',
+ 'ci',
+ 'docs',
+ 'feat',
+ 'fix',
+ 'perf',
+ 'refactor',
+ 'revert',
+ 'style',
+ 'test',
+ 'deps',
+]
+
+const EXTENDED_COMMIT_TYPES = [
+ ...(commitTypeConfig.types ?? []),
+]
+
+const ENV_COMMIT_TYPES = parseCommitTypeList(
+ process.env.COMMIT_TYPES ?? process.env.ADDITIONAL_COMMIT_TYPES ?? ''
+)
+
+const COMMIT_TYPES = Array.from(
+ new Set([...DEFAULT_COMMIT_TYPES, ...EXTENDED_COMMIT_TYPES, ...ENV_COMMIT_TYPES])
+)
+
+const COMMIT_TYPE_SET = new Set(COMMIT_TYPES)
+
+// Component scopes for ProjT Launcher
+const VALID_SCOPES = [
+ 'core',
+ 'ui',
+ 'minecraft',
+ 'modplatform',
+ 'modrinth',
+ 'curseforge',
+ 'ftb',
+ 'technic',
+ 'atlauncher',
+ 'auth',
+ 'java',
+ 'news',
+ 'settings',
+ 'skins',
+ 'translations',
+ 'build',
+ 'ci',
+ 'nix',
+ 'vcpkg',
+ 'deps',
+]
+
+/**
+ * Validate commit message format
+ * Expected format: type(scope): description
+ * @param {string} message - Commit message
+ * @returns {object} Validation result
+ */
+function normalizeCommitType(type) {
+ if (!type) {
+ return ''
+ }
+ const trimmed = type.toLowerCase()
+ const legacyMatch = trimmed.match(/^\d+\.(.+)$/)
+ return legacyMatch ? legacyMatch[1] : trimmed
+}
+
+function validateCommitMessage(message) {
+ const firstLine = message.split('\n')[0]
+
+ // Check for conventional commit format
+ const conventionalMatch = firstLine.match(
+ /^(?<type>[\w.-]+)(?:\((?<scope>[\w-]+)\))?!?:\s*(?<description>.+)$/
+ )
+
+ if (!conventionalMatch) {
+ return {
+ valid: false,
+ severity: 'warning',
+ message: `Commit message doesn't follow conventional format: "${firstLine.substring(0, 50)}..."`,
+ }
+ }
+
+ const { type, scope, description } = conventionalMatch.groups
+ const normalizedType = normalizeCommitType(type)
+
+ // Validate type
+ if (!COMMIT_TYPE_SET.has(normalizedType)) {
+ return {
+ valid: false,
+ severity: 'warning',
+ message: `Unknown commit type "${type}". Valid types: ${COMMIT_TYPES.join(', ')}`,
+ }
+ }
+
+ // Validate scope if present
+ if (scope && !VALID_SCOPES.includes(scope.toLowerCase())) {
+ return {
+ valid: false,
+ severity: 'info',
+ message: `Unknown scope "${scope}". Consider using: ${VALID_SCOPES.slice(0, 5).join(', ')}...`,
+ }
+ }
+
+ // Check description length
+ if (description.length < 10) {
+ return {
+ valid: false,
+ severity: 'warning',
+ message: 'Commit description too short (min 10 chars)',
+ }
+ }
+
+ if (firstLine.length > 140) {
+ return {
+ valid: false,
+ severity: 'info',
+ message: 'First line exceeds 140 characters',
+ }
+ }
+
+ return { valid: true }
+}
+
+/**
+ * Check commit for specific patterns
+ * @param {object} commit - Commit object
+ * @returns {object} Check result
+ */
+function checkCommitPatterns(commit) {
+ const message = commit.message
+ const issues = []
+
+ // Check for WIP markers
+ if (message.match(/\bWIP\b/i)) {
+ issues.push({
+ severity: 'warning',
+ message: 'Commit contains WIP marker',
+ })
+ }
+
+ // Check for fixup/squash commits
+ if (message.match(/^(fixup|squash)!/i)) {
+ issues.push({
+ severity: 'info',
+ message: 'Commit is a fixup/squash commit - remember to rebase before merge',
+ })
+ }
+
+ // Check for merge commits
+ if (message.startsWith('Merge ')) {
+ issues.push({
+ severity: 'info',
+ message: 'Merge commit detected - consider rebasing instead',
+ })
+ }
+
+ // Check for large descriptions without body
+ if (message.split('\n').length === 1 && message.length > 100) {
+ issues.push({
+ severity: 'info',
+ message: 'Long commit message without body - consider adding details in commit body',
+ })
+ }
+
+ return issues
+}
+
+/**
+ * Validate all commits in a PR
+ */
+async function run({ github, context, core, dry }) {
+ await withRateLimit({ github, core }, async (stats) => {
+ stats.prs = 1
+
+ const pull_number = context.payload.pull_request.number
+ const base = context.payload.pull_request.base.ref
+ const baseClassification = classify(base)
+
+ // Get all commits in the PR
+ const commits = await github.paginate(github.rest.pulls.listCommits, {
+ ...context.repo,
+ pull_number,
+ })
+
+ core.info(`Validating ${commits.length} commits for PR #${pull_number}`)
+
+ const results = []
+
+ for (const { sha, commit } of commits) {
+ const commitResults = {
+ sha: sha.substring(0, 7),
+ fullSha: sha,
+ author: commit.author.name,
+ message: commit.message.split('\n')[0],
+ issues: [],
+ }
+
+ // Validate commit message format
+ const formatValidation = validateCommitMessage(commit.message)
+ if (!formatValidation.valid) {
+ commitResults.issues.push({
+ severity: formatValidation.severity,
+ message: formatValidation.message,
+ })
+ }
+
+ // Check for commit patterns
+ const patternIssues = checkCommitPatterns(commit)
+ commitResults.issues.push(...patternIssues)
+
+ results.push(commitResults)
+ }
+
+ // Log results
+ let hasErrors = false
+ let hasWarnings = false
+
+ for (const result of results) {
+ core.startGroup(`Commit ${result.sha}`)
+ core.info(`Author: ${result.author}`)
+ core.info(`Message: ${result.message}`)
+
+ if (result.issues.length === 0) {
+ core.info('✓ No issues found')
+ } else {
+ for (const issue of result.issues) {
+ switch (issue.severity) {
+ case 'error':
+ core.error(issue.message)
+ hasErrors = true
+ break
+ case 'warning':
+ core.warning(issue.message)
+ hasWarnings = true
+ break
+ default:
+ core.info(`ℹ ${issue.message}`)
+ }
+ }
+ }
+ core.endGroup()
+ }
+
+ // If all commits are valid, dismiss any previous reviews
+ if (!hasErrors && !hasWarnings) {
+ await dismissReviews({ github, context, dry })
+ core.info('✓ All commits passed validation')
+ return
+ }
+
+ // Generate summary for issues
+ const issueCommits = results.filter(r => r.issues.length > 0)
+
+ if (issueCommits.length > 0) {
+ const body = [
+ '## Commit Validation Issues',
+ '',
+ 'The following commits have issues that should be addressed:',
+ '',
+ ...issueCommits.flatMap(commit => [
+ `### \`${commit.sha}\`: ${commit.message}`,
+ '',
+ ...commit.issues.map(issue => `- **${issue.severity}**: ${issue.message}`),
+ '',
+ ]),
+ '---',
+ '',
+ '### Commit Message Guidelines',
+ '',
+ 'ProjT Launcher uses [Conventional Commits](https://www.conventionalcommits.org/):',
+ '',
+ '```',
+ 'type(scope): description',
+ '',
+ '[optional body]',
+ '',
+ '[optional footer]',
+ '```',
+ '',
+ `**Types**: ${COMMIT_TYPES.join(', ')}`,
+ '',
+ `**Scopes**: ${VALID_SCOPES.slice(0, 8).join(', ')}, ...`,
+ ].join('\n')
+
+ // Post review only for errors/warnings, not info
+ if (hasErrors || hasWarnings) {
+ await postReview({ github, context, core, dry, body })
+ }
+
+ // Write step summary
+ const fs = require('node:fs')
+ if (process.env.GITHUB_STEP_SUMMARY) {
+ fs.appendFileSync(process.env.GITHUB_STEP_SUMMARY, body)
+ }
+ }
+
+ if (hasErrors) {
+ throw new Error('Commit validation failed with errors')
+ }
+ })
+}
+
+module.exports = run
+module.exports.validateCommitMessage = validateCommitMessage
+module.exports.checkCommitPatterns = checkCommitPatterns
+module.exports.normalizeCommitType = normalizeCommitType