@programinglive/commiter 1.2.6 → 1.2.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/ISSUE_TEMPLATE/bug_report.md +28 -28
- package/.github/ISSUE_TEMPLATE/config.yml +5 -5
- package/.github/ISSUE_TEMPLATE/feature_request.md +20 -20
- package/.github/PULL_REQUEST_TEMPLATE.md +24 -24
- package/.netlify/netlify.toml +25 -0
- package/.netlify/state.json +3 -0
- package/CHANGELOG.md +259 -189
- package/CODE_OF_CONDUCT.md +36 -36
- package/LICENSE +21 -21
- package/PRD.md +96 -91
- package/PUBLISH.md +142 -142
- package/README.md +217 -211
- package/SECURITY.md +30 -30
- package/commitlint.config.cjs +4 -4
- package/docs/CREATE_RELEASES.md +103 -103
- package/docs/QUICK_RELEASE_GUIDE.md +101 -101
- package/docs/release-notes/RELEASE_NOTES.md +233 -177
- package/index.js +199 -196
- package/netlify.toml +2 -0
- package/package.json +96 -97
- package/scripts/create-releases.js +219 -219
- package/scripts/release.cjs +258 -284
- package/scripts/update-release-notes.cjs +182 -182
- package/web/README.md +79 -79
- package/web/css/style.css +963 -963
- package/web/favicon.svg +10 -10
- package/web/index.html +510 -507
- package/web/js/script.js +256 -256
- package/web/og-image.svg +24 -24
- package/scripts/update-web-releases.js +0 -232
- package/scripts/update-web-version.js +0 -37
|
@@ -1,219 +1,219 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Create GitHub Releases from Tags and CHANGELOG
|
|
5
|
-
*
|
|
6
|
-
* This script reads the CHANGELOG.md file and creates GitHub releases
|
|
7
|
-
* for all tags that don't have releases yet.
|
|
8
|
-
*
|
|
9
|
-
* Prerequisites:
|
|
10
|
-
* - GitHub Personal Access Token with 'repo' scope
|
|
11
|
-
* - Set as environment variable: GITHUB_TOKEN
|
|
12
|
-
*
|
|
13
|
-
* Usage:
|
|
14
|
-
* GITHUB_TOKEN=your_token node scripts/create-releases.js
|
|
15
|
-
*/
|
|
16
|
-
|
|
17
|
-
const fs = require('fs');
|
|
18
|
-
const https = require('https');
|
|
19
|
-
const { execSync } = require('child_process');
|
|
20
|
-
|
|
21
|
-
// Configuration
|
|
22
|
-
const REPO_OWNER = 'programinglive';
|
|
23
|
-
const REPO_NAME = 'commiter';
|
|
24
|
-
const CHANGELOG_PATH = './CHANGELOG.md';
|
|
25
|
-
|
|
26
|
-
// GitHub API helper
|
|
27
|
-
function githubRequest(method, path, data = null) {
|
|
28
|
-
const token = process.env.GITHUB_TOKEN;
|
|
29
|
-
|
|
30
|
-
if (!token) {
|
|
31
|
-
console.error('❌ Error: GITHUB_TOKEN environment variable not set');
|
|
32
|
-
console.error('Please set it with: set GITHUB_TOKEN=your_token_here');
|
|
33
|
-
process.exit(1);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
return new Promise((resolve, reject) => {
|
|
37
|
-
const options = {
|
|
38
|
-
hostname: 'api.github.com',
|
|
39
|
-
port: 443,
|
|
40
|
-
path: path,
|
|
41
|
-
method: method,
|
|
42
|
-
headers: {
|
|
43
|
-
'User-Agent': 'Commiter-Release-Script',
|
|
44
|
-
'Authorization': `token ${token}`,
|
|
45
|
-
'Accept': 'application/vnd.github.v3+json',
|
|
46
|
-
'Content-Type': 'application/json'
|
|
47
|
-
}
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
const req = https.request(options, (res) => {
|
|
51
|
-
let body = '';
|
|
52
|
-
res.on('data', (chunk) => body += chunk);
|
|
53
|
-
res.on('end', () => {
|
|
54
|
-
if (res.statusCode >= 200 && res.statusCode < 300) {
|
|
55
|
-
resolve(JSON.parse(body || '{}'));
|
|
56
|
-
} else {
|
|
57
|
-
reject(new Error(`GitHub API error: ${res.statusCode} - ${body}`));
|
|
58
|
-
}
|
|
59
|
-
});
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
req.on('error', reject);
|
|
63
|
-
|
|
64
|
-
if (data) {
|
|
65
|
-
req.write(JSON.stringify(data));
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
req.end();
|
|
69
|
-
});
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// Parse CHANGELOG.md to extract release notes
|
|
73
|
-
function parseChangelog() {
|
|
74
|
-
const content = fs.readFileSync(CHANGELOG_PATH, 'utf-8');
|
|
75
|
-
const releases = {};
|
|
76
|
-
|
|
77
|
-
// Match version headers like: ### [1.1.9](link) (2025-11-24)
|
|
78
|
-
const versionRegex = /### \[(\d+\.\d+\.\d+)\]\([^)]+\) \(([^)]+)\)/g;
|
|
79
|
-
const sections = content.split(versionRegex);
|
|
80
|
-
|
|
81
|
-
// Process matches
|
|
82
|
-
let match;
|
|
83
|
-
const matches = [];
|
|
84
|
-
while ((match = versionRegex.exec(content)) !== null) {
|
|
85
|
-
matches.push({
|
|
86
|
-
version: match[1],
|
|
87
|
-
date: match[2],
|
|
88
|
-
index: match.index
|
|
89
|
-
});
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
// Extract content for each version
|
|
93
|
-
for (let i = 0; i < matches.length; i++) {
|
|
94
|
-
const current = matches[i];
|
|
95
|
-
const next = matches[i + 1];
|
|
96
|
-
|
|
97
|
-
const startIndex = current.index;
|
|
98
|
-
const endIndex = next ? next.index : content.length;
|
|
99
|
-
|
|
100
|
-
let releaseContent = content.substring(startIndex, endIndex).trim();
|
|
101
|
-
|
|
102
|
-
// Remove the version header line
|
|
103
|
-
releaseContent = releaseContent.split('\n').slice(1).join('\n').trim();
|
|
104
|
-
|
|
105
|
-
// Clean up the content
|
|
106
|
-
releaseContent = releaseContent
|
|
107
|
-
.replace(/###\s+\[(\d+\.\d+\.\d+)\].*$/m, '') // Remove next version header if present
|
|
108
|
-
.trim();
|
|
109
|
-
|
|
110
|
-
releases[current.version] = {
|
|
111
|
-
version: current.version,
|
|
112
|
-
date: current.date,
|
|
113
|
-
body: releaseContent || 'See CHANGELOG.md for details.'
|
|
114
|
-
};
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
return releases;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
// Get all local tags
|
|
121
|
-
function getLocalTags() {
|
|
122
|
-
try {
|
|
123
|
-
const output = execSync('git tag -l', { encoding: 'utf-8' });
|
|
124
|
-
return output.trim().split('\n').filter(Boolean);
|
|
125
|
-
} catch (error) {
|
|
126
|
-
console.error('❌ Error getting local tags:', error.message);
|
|
127
|
-
return [];
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
// Get existing GitHub releases
|
|
132
|
-
async function getExistingReleases() {
|
|
133
|
-
try {
|
|
134
|
-
const releases = await githubRequest('GET', `/repos/${REPO_OWNER}/${REPO_NAME}/releases`);
|
|
135
|
-
return new Set(releases.map(r => r.tag_name));
|
|
136
|
-
} catch (error) {
|
|
137
|
-
console.error('❌ Error fetching existing releases:', error.message);
|
|
138
|
-
return new Set();
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
// Create a GitHub release
|
|
143
|
-
async function createRelease(tag, releaseData) {
|
|
144
|
-
const data = {
|
|
145
|
-
tag_name: tag,
|
|
146
|
-
name: `${tag}`,
|
|
147
|
-
body: releaseData.body,
|
|
148
|
-
draft: false,
|
|
149
|
-
prerelease: false
|
|
150
|
-
};
|
|
151
|
-
|
|
152
|
-
try {
|
|
153
|
-
await githubRequest('POST', `/repos/${REPO_OWNER}/${REPO_NAME}/releases`, data);
|
|
154
|
-
console.log(`✅ Created release for ${tag}`);
|
|
155
|
-
return true;
|
|
156
|
-
} catch (error) {
|
|
157
|
-
console.error(`❌ Failed to create release for ${tag}:`, error.message);
|
|
158
|
-
return false;
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
// Main function
|
|
163
|
-
async function main() {
|
|
164
|
-
console.log('🚀 Creating GitHub Releases from CHANGELOG...\n');
|
|
165
|
-
|
|
166
|
-
// Parse changelog
|
|
167
|
-
console.log('📝 Parsing CHANGELOG.md...');
|
|
168
|
-
const releases = parseChangelog();
|
|
169
|
-
console.log(`Found ${Object.keys(releases).length} releases in CHANGELOG\n`);
|
|
170
|
-
|
|
171
|
-
// Get local tags
|
|
172
|
-
console.log('🏷️ Getting local tags...');
|
|
173
|
-
const localTags = getLocalTags();
|
|
174
|
-
console.log(`Found ${localTags.length} local tags\n`);
|
|
175
|
-
|
|
176
|
-
// Get existing GitHub releases
|
|
177
|
-
console.log('🔍 Checking existing GitHub releases...');
|
|
178
|
-
const existingReleases = await getExistingReleases();
|
|
179
|
-
console.log(`Found ${existingReleases.size} existing releases on GitHub\n`);
|
|
180
|
-
|
|
181
|
-
// Create releases for tags that don't have them
|
|
182
|
-
console.log('📦 Creating missing releases...\n');
|
|
183
|
-
let created = 0;
|
|
184
|
-
let skipped = 0;
|
|
185
|
-
|
|
186
|
-
for (const tag of localTags) {
|
|
187
|
-
const version = tag.replace(/^v/, '');
|
|
188
|
-
|
|
189
|
-
if (existingReleases.has(tag)) {
|
|
190
|
-
console.log(`⏭️ Skipping ${tag} (already exists)`);
|
|
191
|
-
skipped++;
|
|
192
|
-
continue;
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
const releaseData = releases[version];
|
|
196
|
-
if (!releaseData) {
|
|
197
|
-
console.log(`⚠️ No changelog entry found for ${tag}, using default message`);
|
|
198
|
-
await createRelease(tag, { body: 'See [CHANGELOG.md](CHANGELOG.md) for details.' });
|
|
199
|
-
created++;
|
|
200
|
-
} else {
|
|
201
|
-
await createRelease(tag, releaseData);
|
|
202
|
-
created++;
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
// Rate limiting: wait 1 second between requests
|
|
206
|
-
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
console.log(`\n✨ Done!`);
|
|
210
|
-
console.log(` Created: ${created}`);
|
|
211
|
-
console.log(` Skipped: ${skipped}`);
|
|
212
|
-
console.log(`\n🔗 View releases at: https://github.com/${REPO_OWNER}/${REPO_NAME}/releases`);
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
// Run the script
|
|
216
|
-
main().catch(error => {
|
|
217
|
-
console.error('\n❌ Script failed:', error.message);
|
|
218
|
-
process.exit(1);
|
|
219
|
-
});
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Create GitHub Releases from Tags and CHANGELOG
|
|
5
|
+
*
|
|
6
|
+
* This script reads the CHANGELOG.md file and creates GitHub releases
|
|
7
|
+
* for all tags that don't have releases yet.
|
|
8
|
+
*
|
|
9
|
+
* Prerequisites:
|
|
10
|
+
* - GitHub Personal Access Token with 'repo' scope
|
|
11
|
+
* - Set as environment variable: GITHUB_TOKEN
|
|
12
|
+
*
|
|
13
|
+
* Usage:
|
|
14
|
+
* GITHUB_TOKEN=your_token node scripts/create-releases.js
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
const fs = require('fs');
|
|
18
|
+
const https = require('https');
|
|
19
|
+
const { execSync } = require('child_process');
|
|
20
|
+
|
|
21
|
+
// Configuration
|
|
22
|
+
const REPO_OWNER = 'programinglive';
|
|
23
|
+
const REPO_NAME = 'commiter';
|
|
24
|
+
const CHANGELOG_PATH = './CHANGELOG.md';
|
|
25
|
+
|
|
26
|
+
// GitHub API helper
|
|
27
|
+
function githubRequest(method, path, data = null) {
|
|
28
|
+
const token = process.env.GITHUB_TOKEN;
|
|
29
|
+
|
|
30
|
+
if (!token) {
|
|
31
|
+
console.error('❌ Error: GITHUB_TOKEN environment variable not set');
|
|
32
|
+
console.error('Please set it with: set GITHUB_TOKEN=your_token_here');
|
|
33
|
+
process.exit(1);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return new Promise((resolve, reject) => {
|
|
37
|
+
const options = {
|
|
38
|
+
hostname: 'api.github.com',
|
|
39
|
+
port: 443,
|
|
40
|
+
path: path,
|
|
41
|
+
method: method,
|
|
42
|
+
headers: {
|
|
43
|
+
'User-Agent': 'Commiter-Release-Script',
|
|
44
|
+
'Authorization': `token ${token}`,
|
|
45
|
+
'Accept': 'application/vnd.github.v3+json',
|
|
46
|
+
'Content-Type': 'application/json'
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const req = https.request(options, (res) => {
|
|
51
|
+
let body = '';
|
|
52
|
+
res.on('data', (chunk) => body += chunk);
|
|
53
|
+
res.on('end', () => {
|
|
54
|
+
if (res.statusCode >= 200 && res.statusCode < 300) {
|
|
55
|
+
resolve(JSON.parse(body || '{}'));
|
|
56
|
+
} else {
|
|
57
|
+
reject(new Error(`GitHub API error: ${res.statusCode} - ${body}`));
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
req.on('error', reject);
|
|
63
|
+
|
|
64
|
+
if (data) {
|
|
65
|
+
req.write(JSON.stringify(data));
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
req.end();
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Parse CHANGELOG.md to extract release notes
|
|
73
|
+
function parseChangelog() {
|
|
74
|
+
const content = fs.readFileSync(CHANGELOG_PATH, 'utf-8');
|
|
75
|
+
const releases = {};
|
|
76
|
+
|
|
77
|
+
// Match version headers like: ### [1.1.9](link) (2025-11-24)
|
|
78
|
+
const versionRegex = /### \[(\d+\.\d+\.\d+)\]\([^)]+\) \(([^)]+)\)/g;
|
|
79
|
+
const sections = content.split(versionRegex);
|
|
80
|
+
|
|
81
|
+
// Process matches
|
|
82
|
+
let match;
|
|
83
|
+
const matches = [];
|
|
84
|
+
while ((match = versionRegex.exec(content)) !== null) {
|
|
85
|
+
matches.push({
|
|
86
|
+
version: match[1],
|
|
87
|
+
date: match[2],
|
|
88
|
+
index: match.index
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Extract content for each version
|
|
93
|
+
for (let i = 0; i < matches.length; i++) {
|
|
94
|
+
const current = matches[i];
|
|
95
|
+
const next = matches[i + 1];
|
|
96
|
+
|
|
97
|
+
const startIndex = current.index;
|
|
98
|
+
const endIndex = next ? next.index : content.length;
|
|
99
|
+
|
|
100
|
+
let releaseContent = content.substring(startIndex, endIndex).trim();
|
|
101
|
+
|
|
102
|
+
// Remove the version header line
|
|
103
|
+
releaseContent = releaseContent.split('\n').slice(1).join('\n').trim();
|
|
104
|
+
|
|
105
|
+
// Clean up the content
|
|
106
|
+
releaseContent = releaseContent
|
|
107
|
+
.replace(/###\s+\[(\d+\.\d+\.\d+)\].*$/m, '') // Remove next version header if present
|
|
108
|
+
.trim();
|
|
109
|
+
|
|
110
|
+
releases[current.version] = {
|
|
111
|
+
version: current.version,
|
|
112
|
+
date: current.date,
|
|
113
|
+
body: releaseContent || 'See CHANGELOG.md for details.'
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return releases;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Get all local tags
|
|
121
|
+
function getLocalTags() {
|
|
122
|
+
try {
|
|
123
|
+
const output = execSync('git tag -l', { encoding: 'utf-8' });
|
|
124
|
+
return output.trim().split('\n').filter(Boolean);
|
|
125
|
+
} catch (error) {
|
|
126
|
+
console.error('❌ Error getting local tags:', error.message);
|
|
127
|
+
return [];
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Get existing GitHub releases
|
|
132
|
+
async function getExistingReleases() {
|
|
133
|
+
try {
|
|
134
|
+
const releases = await githubRequest('GET', `/repos/${REPO_OWNER}/${REPO_NAME}/releases`);
|
|
135
|
+
return new Set(releases.map(r => r.tag_name));
|
|
136
|
+
} catch (error) {
|
|
137
|
+
console.error('❌ Error fetching existing releases:', error.message);
|
|
138
|
+
return new Set();
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Create a GitHub release
|
|
143
|
+
async function createRelease(tag, releaseData) {
|
|
144
|
+
const data = {
|
|
145
|
+
tag_name: tag,
|
|
146
|
+
name: `${tag}`,
|
|
147
|
+
body: releaseData.body,
|
|
148
|
+
draft: false,
|
|
149
|
+
prerelease: false
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
try {
|
|
153
|
+
await githubRequest('POST', `/repos/${REPO_OWNER}/${REPO_NAME}/releases`, data);
|
|
154
|
+
console.log(`✅ Created release for ${tag}`);
|
|
155
|
+
return true;
|
|
156
|
+
} catch (error) {
|
|
157
|
+
console.error(`❌ Failed to create release for ${tag}:`, error.message);
|
|
158
|
+
return false;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Main function
|
|
163
|
+
async function main() {
|
|
164
|
+
console.log('🚀 Creating GitHub Releases from CHANGELOG...\n');
|
|
165
|
+
|
|
166
|
+
// Parse changelog
|
|
167
|
+
console.log('📝 Parsing CHANGELOG.md...');
|
|
168
|
+
const releases = parseChangelog();
|
|
169
|
+
console.log(`Found ${Object.keys(releases).length} releases in CHANGELOG\n`);
|
|
170
|
+
|
|
171
|
+
// Get local tags
|
|
172
|
+
console.log('🏷️ Getting local tags...');
|
|
173
|
+
const localTags = getLocalTags();
|
|
174
|
+
console.log(`Found ${localTags.length} local tags\n`);
|
|
175
|
+
|
|
176
|
+
// Get existing GitHub releases
|
|
177
|
+
console.log('🔍 Checking existing GitHub releases...');
|
|
178
|
+
const existingReleases = await getExistingReleases();
|
|
179
|
+
console.log(`Found ${existingReleases.size} existing releases on GitHub\n`);
|
|
180
|
+
|
|
181
|
+
// Create releases for tags that don't have them
|
|
182
|
+
console.log('📦 Creating missing releases...\n');
|
|
183
|
+
let created = 0;
|
|
184
|
+
let skipped = 0;
|
|
185
|
+
|
|
186
|
+
for (const tag of localTags) {
|
|
187
|
+
const version = tag.replace(/^v/, '');
|
|
188
|
+
|
|
189
|
+
if (existingReleases.has(tag)) {
|
|
190
|
+
console.log(`⏭️ Skipping ${tag} (already exists)`);
|
|
191
|
+
skipped++;
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const releaseData = releases[version];
|
|
196
|
+
if (!releaseData) {
|
|
197
|
+
console.log(`⚠️ No changelog entry found for ${tag}, using default message`);
|
|
198
|
+
await createRelease(tag, { body: 'See [CHANGELOG.md](CHANGELOG.md) for details.' });
|
|
199
|
+
created++;
|
|
200
|
+
} else {
|
|
201
|
+
await createRelease(tag, releaseData);
|
|
202
|
+
created++;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Rate limiting: wait 1 second between requests
|
|
206
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
console.log(`\n✨ Done!`);
|
|
210
|
+
console.log(` Created: ${created}`);
|
|
211
|
+
console.log(` Skipped: ${skipped}`);
|
|
212
|
+
console.log(`\n🔗 View releases at: https://github.com/${REPO_OWNER}/${REPO_NAME}/releases`);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Run the script
|
|
216
|
+
main().catch(error => {
|
|
217
|
+
console.error('\n❌ Script failed:', error.message);
|
|
218
|
+
process.exit(1);
|
|
219
|
+
});
|