bitrise-api-cli 0.0.6 → 0.0.8
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/README.md +14 -76
- package/dist/bash-complete.js +1 -1
- package/dist/{chunk-SUHWPUKK.js → chunk-WFYIERBR.js} +20 -11
- package/dist/cli.js +1 -1
- package/dist/{impl-ESKLIOQY.js → impl-PVD6QYYZ.js} +81 -34
- package/package.json +6 -4
package/README.md
CHANGED
|
@@ -1,87 +1,42 @@
|
|
|
1
1
|
# Bitrise API CLI [![npm][npm-image]][npm-url] ![npm][npm-dl-stats]
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
🚀 An unofficial CLI tool to interact with Bitrise API, making artifact downloads a breeze.
|
|
5
|
-
</div>
|
|
3
|
+
🚀 An unofficial CLI tool for Bitrise API artifact downloads.
|
|
6
4
|
|
|
7
5
|
## 🎯 Features
|
|
8
6
|
|
|
9
|
-
- Download artifacts from
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
- Supports both CLI arguments and environment variables
|
|
7
|
+
- Download artifacts from successful builds
|
|
8
|
+
- Workflow-based retrieval
|
|
9
|
+
- CI/CD integration
|
|
13
10
|
|
|
14
11
|
## 🚀 Quick Start
|
|
15
12
|
|
|
16
13
|
### Download Artifacts
|
|
17
14
|
|
|
18
15
|
```bash
|
|
19
|
-
|
|
20
|
-
npx bitrise-api-cli@latest download --token <BITRISE_TOKEN> --workflow <WORKFLOW_NAME>
|
|
21
|
-
|
|
22
|
-
# If running locally, you'll need to specify the app slug
|
|
23
|
-
npx bitrise-api-cli@latest download \
|
|
24
|
-
--token <BITRISE_TOKEN> \
|
|
25
|
-
--workflow <WORKFLOW_NAME> \
|
|
26
|
-
--slug <APP_SLUG>
|
|
16
|
+
npx bitrise-api-cli@latest download --token <BITRISE_TOKEN> --workflow <WORKFLOW_NAME> [--slug <APP_SLUG>]
|
|
27
17
|
```
|
|
28
18
|
|
|
29
19
|
## 🔐 Configuration
|
|
30
20
|
|
|
31
|
-
### Environment Variables
|
|
32
|
-
|
|
33
|
-
| Variable | Description | Required |
|
|
34
|
-
| -------------------------- | ---------------------------------------- |--------------|
|
|
35
|
-
| `BITRISE_API_CLI_TOKEN` | Your Bitrise API token | Yes |
|
|
36
|
-
| `BITRISE_API_CLI_WORKFLOW` | Workflow name to download artifacts from | Yes |
|
|
37
|
-
| `BITRISE_APP_SLUG` | App Slug (auto-set in Bitrise CI) | only locally |
|
|
21
|
+
### Environment Variables (instead of CLI flags)
|
|
38
22
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
-w, --workflow <WORKFLOW_NAME> workflow to get apk from (or $BITRISE_API_CLI_WORKFLOW)
|
|
45
|
-
-s, --slug <APP_SLUG> bitrise app slug (or $BITRISE_APP_SLUG) see bitrise project url to get slug
|
|
46
|
-
-a, --artifact <ARTIFACT_TYPE> bitrise artifact type "ios-ipa" or "android-apk"
|
|
47
|
-
-d, --debug output extra debugging (default: false)
|
|
48
|
-
-h, --help display help for command
|
|
49
|
-
```
|
|
23
|
+
| Variable | Description | Required |
|
|
24
|
+
| -------------------------- | ------------------------------------ | ------------ |
|
|
25
|
+
| `BITRISE_API_CLI_TOKEN` | Your Bitrise API token | Yes |
|
|
26
|
+
| `BITRISE_API_CLI_WORKFLOW` | Workflow name for artifact downloads | Yes |
|
|
27
|
+
| `BITRISE_APP_SLUG` | App Slug (auto-set in Bitrise CI) | Locally only |
|
|
50
28
|
|
|
51
29
|
## 💡 Examples
|
|
52
30
|
|
|
53
|
-
###
|
|
54
|
-
|
|
55
|
-
```bash
|
|
56
|
-
# Download artifacts from a specific workflow
|
|
57
|
-
npx bitrise-api-cli@latest download \
|
|
58
|
-
--token abc123 \
|
|
59
|
-
--workflow android-release \
|
|
60
|
-
--slug a1b2c3d4e5f6
|
|
61
|
-
|
|
62
|
-
# Enable debug logging
|
|
63
|
-
npx bitrise-api-cli@latest download --debug \
|
|
64
|
-
--token abc123 \
|
|
65
|
-
--workflow android-release
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
### Using Environment Variables
|
|
31
|
+
### Command Line
|
|
69
32
|
|
|
70
33
|
```bash
|
|
71
|
-
|
|
72
|
-
export BITRISE_API_CLI_TOKEN=abc123
|
|
73
|
-
export BITRISE_API_CLI_WORKFLOW=android-release
|
|
74
|
-
export BITRISE_APP_SLUG=a1b2c3d4e5f6
|
|
75
|
-
|
|
76
|
-
# Run the command
|
|
77
|
-
npx bitrise-api-cli@latest download
|
|
34
|
+
npx bitrise-api-cli@latest download --token abc123 --workflow android-release --slug a1b2c3d4e5f6
|
|
78
35
|
```
|
|
79
36
|
|
|
80
|
-
## 🔍 Common Use Cases
|
|
81
|
-
|
|
82
37
|
### CI/CD Integration
|
|
83
38
|
|
|
84
|
-
|
|
39
|
+
Integrate this CLI tool using the "Script" Step to download artifacts from previous builds, eliminating the need to rebuild from source.
|
|
85
40
|
|
|
86
41
|
```yaml
|
|
87
42
|
steps:
|
|
@@ -93,33 +48,16 @@ steps:
|
|
|
93
48
|
--token $BITRISE_API_TOKEN
|
|
94
49
|
```
|
|
95
50
|
|
|
96
|
-
### Local Development
|
|
97
|
-
|
|
98
|
-
Download the latest artifacts to test without building:
|
|
99
|
-
|
|
100
|
-
```bash
|
|
101
|
-
npx bitrise-api-cli@latest download \
|
|
102
|
-
--token $BITRISE_API_TOKEN \
|
|
103
|
-
--workflow android-release \
|
|
104
|
-
--slug $BITRISE_APP_SLUG
|
|
105
|
-
```
|
|
106
|
-
|
|
107
51
|
## 📝 License
|
|
108
52
|
|
|
109
53
|
This project is [MIT](LICENSE) licensed.
|
|
110
54
|
|
|
111
|
-
## 🙏 Acknowledgments
|
|
112
|
-
|
|
113
|
-
- Thanks to [Bitrise](https://www.bitrise.io/) for their excellent API
|
|
114
|
-
- Built with ❤️ for the mobile CI/CD community
|
|
115
|
-
|
|
116
55
|
---
|
|
117
56
|
|
|
118
57
|
<div align="center">
|
|
119
58
|
<p>Found this project useful? Give it a ⭐️!</p>
|
|
120
59
|
</div>
|
|
121
60
|
|
|
122
|
-
|
|
123
61
|
[npm-image]: https://img.shields.io/npm/v/bitrise-api-cli
|
|
124
62
|
[npm-url]: https://www.npmjs.com/package/bitrise-api-cli
|
|
125
63
|
[npm-dl-stats]: https://img.shields.io/npm/dm/bitrise-api-cli
|
package/dist/bash-complete.js
CHANGED
|
@@ -2,17 +2,10 @@ import {
|
|
|
2
2
|
validArtifactTypes
|
|
3
3
|
} from "./chunk-CF25N4TY.js";
|
|
4
4
|
|
|
5
|
-
// src/cli/
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
// package.json
|
|
9
|
-
var name = "bitrise-api-cli";
|
|
10
|
-
var version = "0.0.6";
|
|
11
|
-
var description = "bitrise api CLI wrapper";
|
|
12
|
-
|
|
13
|
-
// src/cli/app.ts
|
|
5
|
+
// src/cli/commands/download/command.ts
|
|
6
|
+
import { buildCommand } from "@stricli/core";
|
|
14
7
|
var downloadCommand = buildCommand({
|
|
15
|
-
loader: async () => import("./impl-
|
|
8
|
+
loader: async () => import("./impl-PVD6QYYZ.js"),
|
|
16
9
|
parameters: {
|
|
17
10
|
flags: {
|
|
18
11
|
artifact: {
|
|
@@ -46,6 +39,11 @@ var downloadCommand = buildCommand({
|
|
|
46
39
|
default: process.env["BITRISE_API_CLI_WORKFLOW"],
|
|
47
40
|
parse: String,
|
|
48
41
|
placeholder: "WORKFLOW_NAME"
|
|
42
|
+
},
|
|
43
|
+
install: {
|
|
44
|
+
kind: "boolean",
|
|
45
|
+
brief: "Install the downloaded artifact [android only]",
|
|
46
|
+
default: false
|
|
49
47
|
}
|
|
50
48
|
},
|
|
51
49
|
aliases: {
|
|
@@ -53,13 +51,24 @@ var downloadCommand = buildCommand({
|
|
|
53
51
|
d: "debug",
|
|
54
52
|
s: "slug",
|
|
55
53
|
t: "token",
|
|
56
|
-
w: "workflow"
|
|
54
|
+
w: "workflow",
|
|
55
|
+
i: "install"
|
|
57
56
|
}
|
|
58
57
|
},
|
|
59
58
|
docs: {
|
|
60
59
|
brief: "Download bitrise artifacts. Most parameters can be set via environment variables."
|
|
61
60
|
}
|
|
62
61
|
});
|
|
62
|
+
|
|
63
|
+
// src/cli/app.ts
|
|
64
|
+
import { buildApplication, buildRouteMap } from "@stricli/core";
|
|
65
|
+
|
|
66
|
+
// package.json
|
|
67
|
+
var name = "bitrise-api-cli";
|
|
68
|
+
var version = "0.0.8";
|
|
69
|
+
var description = "bitrise api CLI wrapper";
|
|
70
|
+
|
|
71
|
+
// src/cli/app.ts
|
|
63
72
|
var root = buildRouteMap({
|
|
64
73
|
routes: {
|
|
65
74
|
download: downloadCommand
|
package/dist/cli.js
CHANGED
|
@@ -36,6 +36,55 @@ var isEnvValid = () => {
|
|
|
36
36
|
}
|
|
37
37
|
return true;
|
|
38
38
|
};
|
|
39
|
+
var setupEnv = (flags) => {
|
|
40
|
+
if (flags.debug) console.log(JSON.stringify(flags, null, 2));
|
|
41
|
+
saveOptions(flags);
|
|
42
|
+
return isEnvValid();
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
// src/device/install/android.ts
|
|
46
|
+
import { exec } from "teen_process";
|
|
47
|
+
var adbPath = `${process.env.ANDROID_HOME}/platform-tools/adb`;
|
|
48
|
+
var installApk = async (apkPath) => {
|
|
49
|
+
const startTime = Date.now();
|
|
50
|
+
try {
|
|
51
|
+
const { stdout, stderr, code } = await exec(adbPath, ["install", "-r", "-d", apkPath]);
|
|
52
|
+
if (code !== 0) throw new Error(`Failed to install apk error code: ${code}`);
|
|
53
|
+
return { duration: Date.now() - startTime };
|
|
54
|
+
} catch (e) {
|
|
55
|
+
if (e instanceof Error && "stderr" in e) throw new Error(`${e.stderr}`);
|
|
56
|
+
throw new Error("unknown Error");
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
// src/helper/formatTime.ts
|
|
61
|
+
var formatTime = (timeMs) => {
|
|
62
|
+
if (timeMs < 1e3) return `${timeMs}ms`;
|
|
63
|
+
return `${(timeMs / 1e3).toFixed(1)}s`;
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
// src/helper/oraHelper.ts
|
|
67
|
+
var getOraTexts = (label) => ({
|
|
68
|
+
text: label,
|
|
69
|
+
failText: (error) => `${label} failed with error: ${error.message}`,
|
|
70
|
+
successText: (data) => {
|
|
71
|
+
if (data && typeof data === "object" && "duration" in data && typeof data.duration === "number") {
|
|
72
|
+
return `${label} [took ${formatTime(data.duration)}]`;
|
|
73
|
+
}
|
|
74
|
+
return label;
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
// src/device/install/run.ts
|
|
79
|
+
import { oraPromise } from "ora";
|
|
80
|
+
var installApp = async ({ file, flags }) => {
|
|
81
|
+
if (flags.artifact === "android-apk") {
|
|
82
|
+
if (!file.endsWith(".apk")) return console.log("Artifact is not an APK file");
|
|
83
|
+
await oraPromise(installApk(file), getOraTexts("installing APK"));
|
|
84
|
+
} else {
|
|
85
|
+
return console.log("Installing on iOS is not supported yet");
|
|
86
|
+
}
|
|
87
|
+
};
|
|
39
88
|
|
|
40
89
|
// src/generated_api/runtime.ts
|
|
41
90
|
var BASE_PATH = "https://api.bitrise.io/v0.1".replace(/\/+$/, "");
|
|
@@ -1436,12 +1485,20 @@ var api = {
|
|
|
1436
1485
|
// src/helper/debugLog.ts
|
|
1437
1486
|
var debugLog = (label, data) => console.log(`${label}: ${JSON.stringify(data, void 0, 2)}`);
|
|
1438
1487
|
|
|
1488
|
+
// src/helper/ensureMandatoryFields.ts
|
|
1489
|
+
var ensureMandatoryFields = (input, mandatory) => {
|
|
1490
|
+
for (const field of mandatory) {
|
|
1491
|
+
if (!input[field]) throw new Error(`Missing mandatory field: ${String(field)}`);
|
|
1492
|
+
}
|
|
1493
|
+
return input;
|
|
1494
|
+
};
|
|
1495
|
+
|
|
1439
1496
|
// src/bitrise/fetchArtifact.ts
|
|
1440
1497
|
var fetchArtifact = async ({ artifactSlug, buildSlug }) => {
|
|
1441
1498
|
const { data } = await api.buildArtifact().artifactShow({ appSlug: ENV.appSlug, artifactSlug, buildSlug });
|
|
1442
1499
|
if (!data) throw new Error("buildArtifact().artifactShow returned no data");
|
|
1443
1500
|
if (ENV.debug) debugLog("artifact", data);
|
|
1444
|
-
return data;
|
|
1501
|
+
return ensureMandatoryFields(data, ["expiringDownloadUrl"]);
|
|
1445
1502
|
};
|
|
1446
1503
|
|
|
1447
1504
|
// src/bitrise/fetchArtifactList.ts
|
|
@@ -1453,10 +1510,9 @@ var fetchArtifactList = async (buildSlug) => {
|
|
|
1453
1510
|
var fetchArtifactByType = async (buildSlug) => {
|
|
1454
1511
|
const artifactInfo = await fetchArtifactList(buildSlug);
|
|
1455
1512
|
const artifactMetadata = artifactInfo.find((artifact) => artifact.artifactType === ENV.artifactType);
|
|
1456
|
-
if (!artifactMetadata || !artifactMetadata.slug)
|
|
1457
|
-
throw new Error(`Found no ${ENV.artifactType} in build Slug: ${buildSlug}`);
|
|
1458
1513
|
if (ENV.debug) debugLog("artifactMetadata", artifactMetadata);
|
|
1459
|
-
|
|
1514
|
+
if (!artifactMetadata) throw new Error(`Found no ${ENV.artifactType} in build Slug: ${buildSlug}`);
|
|
1515
|
+
return ensureMandatoryFields(artifactMetadata, ["slug", "title"]);
|
|
1460
1516
|
};
|
|
1461
1517
|
|
|
1462
1518
|
// src/bitrise/fetchBuilds.ts
|
|
@@ -1470,7 +1526,7 @@ var getLatestBuild = async () => {
|
|
|
1470
1526
|
const latestBuild = builds.find((build) => build.triggeredWorkflow === ENV.workflow);
|
|
1471
1527
|
if (!latestBuild) throw `Found no build with the workflow: "${ENV.workflow}"`;
|
|
1472
1528
|
if (ENV.debug) debugLog("latestBuild", latestBuild);
|
|
1473
|
-
return latestBuild;
|
|
1529
|
+
return ensureMandatoryFields(latestBuild, ["slug"]);
|
|
1474
1530
|
};
|
|
1475
1531
|
|
|
1476
1532
|
// src/helper/formatBytes.ts
|
|
@@ -1486,18 +1542,13 @@ var formatBytes = (bytes) => {
|
|
|
1486
1542
|
};
|
|
1487
1543
|
|
|
1488
1544
|
// src/downloadLatestArtifacts.ts
|
|
1489
|
-
import { oraPromise } from "ora";
|
|
1545
|
+
import { oraPromise as oraPromise2 } from "ora";
|
|
1490
1546
|
|
|
1491
1547
|
// src/helper/downloadFile.ts
|
|
1492
1548
|
import { createWriteStream } from "node:fs";
|
|
1493
1549
|
import { get } from "node:https";
|
|
1494
1550
|
import { pipeline } from "node:stream/promises";
|
|
1495
1551
|
import { URL } from "node:url";
|
|
1496
|
-
|
|
1497
|
-
// src/helper/formatTime.ts
|
|
1498
|
-
var formatTime = (timeMs) => `${(timeMs / 1e3).toFixed(1)}s`;
|
|
1499
|
-
|
|
1500
|
-
// src/helper/downloadFile.ts
|
|
1501
1552
|
var downloadFile = async (fileUrl, outputLocationPath) => {
|
|
1502
1553
|
const startTime = Date.now();
|
|
1503
1554
|
const url = new URL(fileUrl);
|
|
@@ -1511,39 +1562,35 @@ var downloadFile = async (fileUrl, outputLocationPath) => {
|
|
|
1511
1562
|
}).on("error", reject);
|
|
1512
1563
|
});
|
|
1513
1564
|
await pipeline(response, createWriteStream(outputLocationPath));
|
|
1514
|
-
return { duration:
|
|
1565
|
+
return { file: outputLocationPath, duration: Date.now() - startTime };
|
|
1515
1566
|
};
|
|
1516
1567
|
|
|
1517
1568
|
// src/downloadLatestArtifacts.ts
|
|
1518
1569
|
var downloadLatestArtifacts = async (options) => {
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
if (!latestBuild.slug) throw Error("Latest build contained no Slug");
|
|
1526
|
-
const artifactMetadata = await oraPromise(fetchArtifactByType(latestBuild.slug), {
|
|
1527
|
-
text: "fetching artifact list",
|
|
1528
|
-
failText: (error) => `fetching artifact list failed with error: ${error}`
|
|
1529
|
-
});
|
|
1530
|
-
if (!artifactMetadata.slug || !artifactMetadata.title) throw Error("Artifact was Invalid");
|
|
1531
|
-
const artifact = await oraPromise(
|
|
1570
|
+
const latestBuild = await oraPromise2(getLatestBuild(), getOraTexts("fetching latest build"));
|
|
1571
|
+
const artifactMetadata = await oraPromise2(
|
|
1572
|
+
fetchArtifactByType(latestBuild.slug),
|
|
1573
|
+
getOraTexts("fetching artifact list")
|
|
1574
|
+
);
|
|
1575
|
+
const artifact = await oraPromise2(
|
|
1532
1576
|
fetchArtifact({ artifactSlug: artifactMetadata.slug, buildSlug: latestBuild.slug }),
|
|
1533
|
-
|
|
1577
|
+
getOraTexts("fetching artifact metadata")
|
|
1534
1578
|
);
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
text: `downloading apk file (${fileSize})`,
|
|
1539
|
-
successText: ({ duration }) => `downloading apk file took ${duration}`
|
|
1579
|
+
const { file } = await oraPromise2(downloadFile(artifact.expiringDownloadUrl, artifactMetadata.title), {
|
|
1580
|
+
...getOraTexts("downloading apk file"),
|
|
1581
|
+
text: `downloading apk file [${formatBytes(artifact.fileSizeBytes ?? 0)}]`
|
|
1540
1582
|
});
|
|
1583
|
+
return file;
|
|
1541
1584
|
};
|
|
1542
1585
|
|
|
1543
|
-
// src/cli/impl.ts
|
|
1586
|
+
// src/cli/commands/download/impl.ts
|
|
1544
1587
|
async function impl_default(flags) {
|
|
1545
|
-
if (flags
|
|
1546
|
-
|
|
1588
|
+
if (!setupEnv(flags)) return process.exit(1);
|
|
1589
|
+
const file = await downloadLatestArtifacts(flags);
|
|
1590
|
+
if (flags.install) {
|
|
1591
|
+
await installApp({ file, flags }).catch(() => {
|
|
1592
|
+
});
|
|
1593
|
+
}
|
|
1547
1594
|
}
|
|
1548
1595
|
export {
|
|
1549
1596
|
impl_default as default
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bitrise-api-cli",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.8",
|
|
4
4
|
"description": "bitrise api CLI wrapper",
|
|
5
5
|
"module": "index",
|
|
6
6
|
"type": "module",
|
|
@@ -23,15 +23,17 @@
|
|
|
23
23
|
"prepublishOnly": "bun i && bun run lint:CI && tsc && bun run build"
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"@stricli/core": "1.1.1",
|
|
27
26
|
"@stricli/auto-complete": "1.1.1",
|
|
28
|
-
"
|
|
27
|
+
"@stricli/core": "1.1.1",
|
|
28
|
+
"ora": "8.2.0",
|
|
29
|
+
"teen_process": "2.2.3"
|
|
29
30
|
},
|
|
30
31
|
"devDependencies": {
|
|
31
32
|
"@biomejs/biome": "1.9.4",
|
|
32
33
|
"@openapitools/openapi-generator-cli": "2.16.3",
|
|
33
34
|
"@types/bun": "latest",
|
|
34
|
-
"
|
|
35
|
+
"@types/teen_process": "^2.0.4",
|
|
36
|
+
"tsup": "8.4.0",
|
|
35
37
|
"typescript": "5.7.3"
|
|
36
38
|
}
|
|
37
39
|
}
|