Skip to content

Commit ebf8f52

Browse files
committed
Add upload and delete command
1 parent 64c629d commit ebf8f52

2 files changed

Lines changed: 110 additions & 111 deletions

File tree

package.json

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "github-release-cli",
3-
"version": "0.2.2",
4-
"description": "A command-line tool for uploading release assets to a GitHub repository",
3+
"version": "0.3.0",
4+
"description": "A command-line tool for managing release assets on a GitHub repository",
55
"homepage": "https://github.com/cheton/github-release-cli",
66
"author": "Cheton Wu <cheton@gmail.com>",
77
"bin": {
@@ -30,7 +30,8 @@
3030
"dependencies": {
3131
"babel-runtime": "^6.22.0",
3232
"commander": "^2.9.0",
33-
"github": "^8.1.1"
33+
"github": "^8.1.1",
34+
"minimatch": "^3.0.3"
3435
},
3536
"devDependencies": {
3637
"babel-cli": "^6.22.2",

src/index.js

Lines changed: 106 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,22 @@
33
import path from 'path';
44
import GitHubApi from 'github';
55
import program from 'commander';
6+
import minimatch from 'minimatch';
67
import pkg from '../package.json';
78

89
program
910
.version(pkg.version)
11+
.usage('<command> [<args>]')
1012
.option('-T, --token <token>', 'OAuth2 token')
1113
.option('-o, --owner <owner>', 'owner')
1214
.option('-r, --repo <repo>', 'repo')
1315
.option('-t, --tag <tag>', 'tag')
1416
.option('-n, --name <name>', 'name')
15-
.option('-b, --body <body>', 'body', false)
16-
.parse(process.argv);
17+
.option('-b, --body <body>', 'body', false);
18+
19+
program.parse(process.argv);
20+
21+
const [command, ...args] = program.args;
1722

1823
const github = new GitHubApi({
1924
version: '3.0.0',
@@ -22,7 +27,6 @@ const github = new GitHubApi({
2227
'user-agent': 'GitHub-Release-App'
2328
}
2429
});
25-
const files = program.args;
2630

2731
github.authenticate({
2832
type: 'oauth',
@@ -77,124 +81,118 @@ const uploadAsset = (options) => {
7781
});
7882
};
7983

80-
const main = async () => {
81-
const { owner, repo, tag, name, body } = program;
84+
const fn = {
85+
'upload': async () => {
86+
const { owner, repo, tag, name, body } = program;
87+
const files = args;
88+
let release;
8289

83-
try {
84-
console.log('> releases#getReleaseByTag');
85-
let release = await getReleaseByTag({
86-
owner: owner,
87-
repo: repo,
88-
tag: tag
89-
});
90-
if (!release) {
91-
console.log('> releases#createRelease');
92-
release = await createRelease({
90+
try {
91+
console.log('> releases#getReleaseByTag');
92+
release = await getReleaseByTag({
9393
owner: owner,
9494
repo: repo,
95-
tag_name: tag,
96-
name: name || tag,
97-
body: body || ''
95+
tag: tag
9896
});
99-
console.log('ok', release);
100-
} else if (release.body !== body) {
101-
console.log('> releases#editRelease');
102-
let releaseOptions = {
103-
owner: owner,
104-
repo: repo,
105-
id: release.id,
106-
tag_name: tag,
107-
name: name || tag
108-
};
109-
if (body) {
110-
releaseOptions.body = body || '';
111-
}
112-
release = await editRelease(releaseOptions);
113-
console.log('ok', release);
97+
} catch (err) {
98+
// Ignore
11499
}
115100

116-
console.log('> releases#getAssets');
117-
let assets = await getAssets({
118-
owner: owner,
119-
repo: repo,
120-
id: release.id
121-
});
122-
console.log('assets=%d', assets.length);
123-
124-
assets = assets.filter(asset => {
125-
// Example:
126-
// 'cnc-1.1.0-latest-08c256a-linux-x64.tar.gz'
127-
// ["cnc-1.1.0-latest-08c256a-linux-x64.tar.gz", "cnc", "1.1.0-latest-08c256a", "linux", "x64", "tar.gz"]
128-
const pattern = new RegExp(/([a-zA-Z0-9][a-zA-Z0-9\-]*)\-(\d+\.\d+\.\d+(?:\-[a-zA-Z0-9][a-zA-Z0-9\-]*)?)(?:\-(mac|win|linux|tinyweb))(?:(?:\-([a-zA-Z0-9_\-]+))?\.(.*))/);
129-
130-
return files.some(file => {
131-
let r1 = asset.name.match(pattern);
132-
let r2 = path.basename(file).match(pattern);
133-
134-
if ((r1 === null) || (r2 === null)) {
135-
console.error('Unable to match file: asset="%s", file="%s"', asset.name, path.basename(file));
136-
return false;
137-
}
138-
139-
// 0: full
140-
// 1: name
141-
// 2: version
142-
// 3: platform
143-
// 4: arch
144-
// 5: extname
145-
146-
// Skip checking for #0 (full) and #2 (version)
147-
r1[0] = r1[2] = undefined;
148-
r2[0] = r2[2] = undefined;
149-
150-
// compact
151-
r1 = r1.filter(r => r !== undefined && r !== null);
152-
r2 = r2.filter(r => r !== undefined && r !== null);
153-
154-
// compare two arrays
155-
return (r1.length === r2.length) && r1.every((v, i) => v === r2[i]);
156-
});
157-
});
158-
159-
if (assets.length > 0) {
160-
console.log('> releases#deleteAsset');
161-
for (let i = 0; i < assets.length; ++i) {
162-
const asset = assets[i];
163-
console.log('#%d', i + 1, {
164-
id: asset.id,
165-
name: asset.name,
166-
label: asset.label,
167-
state: asset.state,
168-
size: asset.size,
169-
download_count: asset.download_count,
170-
created_at: asset.created_at,
171-
updated_at: asset.updated_at
172-
});
173-
await deleteAsset({
101+
try {
102+
if (!release) {
103+
console.log('> releases#createRelease');
104+
release = await createRelease({
174105
owner: owner,
175106
repo: repo,
176-
id: asset.id
107+
tag_name: tag,
108+
name: name || tag,
109+
body: body || ''
177110
});
178-
}
179-
}
180-
181-
if (files.length > 0) {
182-
console.log('> releases#uploadAsset');
183-
for (let i = 0; i < files.length; ++i) {
184-
const file = files[i];
185-
console.log('#%d name="%s" filePath="%s"', i + 1, path.basename(file), file);
186-
await uploadAsset({
111+
} else if (body && (release.body !== body)) {
112+
console.log('> releases#editRelease');
113+
let releaseOptions = {
187114
owner: owner,
188115
repo: repo,
189116
id: release.id,
190-
filePath: file,
191-
name: path.basename(file)
192-
});
117+
tag_name: tag,
118+
name: name || tag,
119+
body: body || ''
120+
};
121+
release = await editRelease(releaseOptions);
122+
}
123+
124+
if (files.length > 0) {
125+
console.log('> releases#uploadAsset');
126+
for (let i = 0; i < files.length; ++i) {
127+
const file = files[i];
128+
console.log('#%d name="%s" filePath="%s"', i + 1, path.basename(file), file);
129+
await uploadAsset({
130+
owner: owner,
131+
repo: repo,
132+
id: release.id,
133+
filePath: file,
134+
name: path.basename(file)
135+
});
136+
}
193137
}
138+
} catch (err) {
139+
console.error(err);
140+
}
141+
},
142+
'delete': async () => {
143+
const { owner, repo, tag, name, body } = program;
144+
const patterns = args;
145+
let release;
146+
147+
try {
148+
console.log('> releases#getReleaseByTag');
149+
release = await getReleaseByTag({
150+
owner: owner,
151+
repo: repo,
152+
tag: tag
153+
});
154+
} catch (err) {
155+
console.error(err);
156+
return;
157+
}
158+
159+
try {
160+
console.log('> releases#getAssets');
161+
const assets = await getAssets({
162+
owner: owner,
163+
repo: repo,
164+
id: release.id
165+
});
166+
const deleteAssets = assets.filter(asset => {
167+
return patterns.some(pattern => minimatch(asset.name, pattern));
168+
});
169+
console.log('assets=%d, deleteAssets=%d', assets.length, deleteAssets.length);
170+
171+
if (deleteAssets.length > 0) {
172+
console.log('> releases#deleteAsset');
173+
for (let i = 0; i < deleteAssets.length; ++i) {
174+
const asset = deleteAssets[i];
175+
console.log('#%d', i + 1, {
176+
id: asset.id,
177+
name: asset.name,
178+
label: asset.label,
179+
state: asset.state,
180+
size: asset.size,
181+
download_count: asset.download_count,
182+
created_at: asset.created_at,
183+
updated_at: asset.updated_at
184+
});
185+
await deleteAsset({
186+
owner: owner,
187+
repo: repo,
188+
id: asset.id
189+
});
190+
}
191+
}
192+
} catch (err) {
193+
console.error(err);
194194
}
195-
} catch (err) {
196-
console.error(err);
197195
}
198-
};
196+
}[command];
199197

200-
main();
198+
typeof fn === 'function' && fn();

0 commit comments

Comments
 (0)