@programinglive/commiter 1.2.12 → 1.2.16

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.
@@ -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
+ });