bitrise-api-cli 0.0.4 → 0.0.7

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 CHANGED
@@ -1,87 +1,42 @@
1
- # Bitrise API CLI [![npm version](https://badge.fury.io/js/bitrise-api-cli.svg)](https://www.npmjs.com/package/bitrise-api-cli)
1
+ # Bitrise API CLI [![npm][npm-image]][npm-url] ![npm][npm-dl-stats]
2
2
 
3
- <div align="center">
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 your latest successful builds
10
- - Simple workflow-based artifact retrieval
11
- - Easy integration with CI/CD pipelines
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
- # Using npx (recommended)
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
- ### CLI Options
40
-
41
- ```bash
42
- Options:
43
- -t, --token <TOKEN> bitrise api token (or $BITRISE_API_CLI_TOKEN)
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
- ### Using Command Line Arguments
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
- # Set environment variables
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
- Perfect for downloading artifacts from previous builds instead of rebuilding from source:
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,28 +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>
60
+
61
+ [npm-image]: https://img.shields.io/npm/v/bitrise-api-cli
62
+ [npm-url]: https://www.npmjs.com/package/bitrise-api-cli
63
+ [npm-dl-stats]: https://img.shields.io/npm/dm/bitrise-api-cli
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ app,
4
+ buildContext
5
+ } from "./chunk-4KG3SWIX.js";
6
+ import "./chunk-CF25N4TY.js";
7
+
8
+ // src/cli/bin/bash-complete.ts
9
+ import { proposeCompletions } from "@stricli/core";
10
+ var inputs = process.argv.slice(3);
11
+ if (process.env["COMP_LINE"]?.endsWith(" ")) {
12
+ inputs.push("");
13
+ }
14
+ await proposeCompletions(app, inputs, buildContext(process));
15
+ try {
16
+ for (const { completion } of await proposeCompletions(app, inputs, buildContext(process))) {
17
+ process.stdout.write(`${completion}
18
+ `);
19
+ }
20
+ } catch {
21
+ }
@@ -0,0 +1,103 @@
1
+ import {
2
+ validArtifactTypes
3
+ } from "./chunk-CF25N4TY.js";
4
+
5
+ // src/cli/commands/download/command.ts
6
+ import { buildCommand } from "@stricli/core";
7
+ var downloadCommand = buildCommand({
8
+ loader: async () => import("./impl-5KTEXOJU.js"),
9
+ parameters: {
10
+ flags: {
11
+ artifact: {
12
+ kind: "enum",
13
+ brief: 'bitrise artifact type "ios-ipa" or "android-apk"',
14
+ default: "android-apk",
15
+ values: validArtifactTypes
16
+ },
17
+ debug: {
18
+ kind: "boolean",
19
+ brief: "Number of times to say hello",
20
+ default: false
21
+ },
22
+ slug: {
23
+ kind: "parsed",
24
+ brief: "bitrise app slug (or $BITRISE_APP_SLUG) see bitrise project url to get slug",
25
+ default: process.env["BITRISE_APP_SLUG"],
26
+ parse: String,
27
+ placeholder: "APP_SLUG"
28
+ },
29
+ token: {
30
+ kind: "parsed",
31
+ brief: "bitrise api token (or $BITRISE_API_CLI_TOKEN)",
32
+ default: process.env["BITRISE_API_CLI_TOKEN"],
33
+ parse: String,
34
+ placeholder: "API_TOKEN"
35
+ },
36
+ workflow: {
37
+ kind: "parsed",
38
+ brief: "workflow to get apk from (or $BITRISE_API_CLI_WORKFLOW)",
39
+ default: process.env["BITRISE_API_CLI_WORKFLOW"],
40
+ parse: String,
41
+ placeholder: "WORKFLOW_NAME"
42
+ },
43
+ install: {
44
+ kind: "boolean",
45
+ brief: "Install the downloaded artifact [android only]",
46
+ default: false
47
+ }
48
+ },
49
+ aliases: {
50
+ a: "artifact",
51
+ d: "debug",
52
+ s: "slug",
53
+ t: "token",
54
+ w: "workflow",
55
+ i: "install"
56
+ }
57
+ },
58
+ docs: {
59
+ brief: "Download bitrise artifacts. Most parameters can be set via environment variables."
60
+ }
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.7";
69
+ var description = "bitrise api CLI wrapper";
70
+
71
+ // src/cli/app.ts
72
+ var root = buildRouteMap({
73
+ routes: {
74
+ download: downloadCommand
75
+ },
76
+ docs: {
77
+ brief: description
78
+ }
79
+ });
80
+ var app = buildApplication(root, {
81
+ name,
82
+ versionInfo: {
83
+ currentVersion: version
84
+ }
85
+ });
86
+
87
+ // src/cli/context.ts
88
+ import fs from "node:fs";
89
+ import os from "node:os";
90
+ import path from "node:path";
91
+ function buildContext(process2) {
92
+ return {
93
+ process: process2,
94
+ os,
95
+ fs,
96
+ path
97
+ };
98
+ }
99
+
100
+ export {
101
+ app,
102
+ buildContext
103
+ };
@@ -0,0 +1,6 @@
1
+ // src/cliOptions.types.ts
2
+ var validArtifactTypes = ["android-apk", "ios-ipa"];
3
+
4
+ export {
5
+ validArtifactTypes
6
+ };
package/dist/cli.js ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ app,
4
+ buildContext
5
+ } from "./chunk-4KG3SWIX.js";
6
+ import "./chunk-CF25N4TY.js";
7
+
8
+ // src/cli/bin/cli.ts
9
+ import { run } from "@stricli/core";
10
+ await run(app, process.argv.slice(2), buildContext(process));
@@ -1,19 +1,42 @@
1
- #!/usr/bin/env node
1
+ import {
2
+ validArtifactTypes
3
+ } from "./chunk-CF25N4TY.js";
2
4
 
3
- // src/bin.ts
4
- import { Command } from "commander";
5
+ // src/device/install/android.ts
6
+ import { exec } from "teen_process";
7
+ var adbPath = `${process.env.ANDROID_HOME}/platform-tools/adb`;
8
+ var installApk = async (apkPath) => {
9
+ const { stdout, stderr, code } = await exec(adbPath, ["install", "-r", "-d", apkPath]);
10
+ if (code !== 0) {
11
+ console.log(stderr);
12
+ throw new Error(`Failed to install apk: ${stderr}`);
13
+ }
14
+ };
5
15
 
6
- // src/cliOptions.types.ts
7
- var validArtifactTypes = ["android-apk", "ios-ipa"];
16
+ // src/device/install/run.ts
17
+ import { oraPromise } from "ora";
18
+ var installApp = async ({ file, flags }) => {
19
+ if (flags.artifact === "android-apk") {
20
+ if (!file.endsWith(".apk")) return console.log("Artifact is not an APK file");
21
+ await oraPromise(installApk(file), {
22
+ text: "installing APK",
23
+ failText: (error) => `installing APK failed with error: ${error}`
24
+ });
25
+ } else {
26
+ return console.log("Installing on iOS is not supported yet");
27
+ }
28
+ };
8
29
 
9
30
  // src/bitrise/env.ts
10
31
  var ENV = {
32
+ debug: false,
11
33
  appSlug: "",
12
34
  token: "",
13
35
  workflow: "",
14
36
  artifactType: "android-apk"
15
37
  };
16
38
  var saveOptions = (options) => {
39
+ ENV.debug = options.debug;
17
40
  ENV.appSlug = options.slug;
18
41
  ENV.token = options.token;
19
42
  ENV.workflow = options.workflow;
@@ -1435,28 +1458,17 @@ var api = {
1435
1458
  builds: () => new BuildsApi(getApiConfig())
1436
1459
  };
1437
1460
 
1461
+ // src/helper/debugLog.ts
1462
+ var debugLog = (label, data) => console.log(`${label}: ${JSON.stringify(data, void 0, 2)}`);
1463
+
1438
1464
  // src/bitrise/fetchArtifact.ts
1439
1465
  var fetchArtifact = async ({ artifactSlug, buildSlug }) => {
1440
1466
  const { data } = await api.buildArtifact().artifactShow({ appSlug: ENV.appSlug, artifactSlug, buildSlug });
1441
1467
  if (!data) throw new Error("buildArtifact().artifactShow returned no data");
1468
+ if (ENV.debug) debugLog("artifact", data);
1442
1469
  return data;
1443
1470
  };
1444
1471
 
1445
- // src/helper/formatBytes.ts
1446
- var decimals = 0;
1447
- var formatBytes = (bytes) => {
1448
- if (bytes === 0) return "0 Bytes";
1449
- const k = 1024;
1450
- const dm = decimals || 2;
1451
- const sizes = ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
1452
- const i = Math.floor(Math.log(bytes) / Math.log(k));
1453
- const formattedSize = Number.parseFloat((bytes / k ** i).toFixed(dm));
1454
- return `${formattedSize} ${sizes[i]}`;
1455
- };
1456
-
1457
- // src/downloadLatestArtifacts.ts
1458
- import { oraPromise } from "ora";
1459
-
1460
1472
  // src/bitrise/fetchArtifactList.ts
1461
1473
  var fetchArtifactList = async (buildSlug) => {
1462
1474
  const { data } = await api.buildArtifact().artifactList({ appSlug: ENV.appSlug, buildSlug });
@@ -1468,32 +1480,51 @@ var fetchArtifactByType = async (buildSlug) => {
1468
1480
  const artifactMetadata = artifactInfo.find((artifact) => artifact.artifactType === ENV.artifactType);
1469
1481
  if (!artifactMetadata || !artifactMetadata.slug)
1470
1482
  throw new Error(`Found no ${ENV.artifactType} in build Slug: ${buildSlug}`);
1483
+ if (ENV.debug) debugLog("artifactMetadata", artifactMetadata);
1471
1484
  return artifactMetadata;
1472
1485
  };
1473
1486
 
1474
- // src/helper/debugLog.ts
1475
- var debugLog = (label, data) => console.log(`${label}: ${JSON.stringify(data, void 0, 2)}`);
1476
-
1477
1487
  // src/bitrise/fetchBuilds.ts
1478
1488
  var getAllBuilds = async () => {
1479
1489
  const { data } = await api.builds().buildList({ appSlug: ENV.appSlug, status: 1 });
1480
1490
  if (!data) throw Error("no build data");
1481
1491
  return data;
1482
1492
  };
1483
- var getLatestBuild = async (options) => {
1493
+ var getLatestBuild = async () => {
1484
1494
  const builds = await getAllBuilds();
1485
1495
  const latestBuild = builds.find((build) => build.triggeredWorkflow === ENV.workflow);
1486
1496
  if (!latestBuild) throw `Found no build with the workflow: "${ENV.workflow}"`;
1487
- if (options.debug) debugLog("latestBuild", latestBuild);
1497
+ if (ENV.debug) debugLog("latestBuild", latestBuild);
1488
1498
  return latestBuild;
1489
1499
  };
1490
1500
 
1501
+ // src/helper/formatBytes.ts
1502
+ var decimals = 0;
1503
+ var formatBytes = (bytes) => {
1504
+ if (bytes === 0) return "0 Bytes";
1505
+ const k = 1024;
1506
+ const dm = decimals || 2;
1507
+ const sizes = ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
1508
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
1509
+ const formattedSize = Number.parseFloat((bytes / k ** i).toFixed(dm));
1510
+ return `${formattedSize} ${sizes[i]}`;
1511
+ };
1512
+
1513
+ // src/downloadLatestArtifacts.ts
1514
+ import { oraPromise as oraPromise2 } from "ora";
1515
+
1491
1516
  // src/helper/downloadFile.ts
1492
1517
  import { createWriteStream } from "node:fs";
1493
1518
  import { get } from "node:https";
1494
1519
  import { pipeline } from "node:stream/promises";
1495
1520
  import { URL } from "node:url";
1521
+
1522
+ // src/helper/formatTime.ts
1523
+ var formatTime = (timeMs) => `${(timeMs / 1e3).toFixed(1)}s`;
1524
+
1525
+ // src/helper/downloadFile.ts
1496
1526
  var downloadFile = async (fileUrl, outputLocationPath) => {
1527
+ const startTime = Date.now();
1497
1528
  const url = new URL(fileUrl);
1498
1529
  const response = await new Promise((resolve, reject) => {
1499
1530
  get(url, (res) => {
@@ -1505,54 +1536,44 @@ var downloadFile = async (fileUrl, outputLocationPath) => {
1505
1536
  }).on("error", reject);
1506
1537
  });
1507
1538
  await pipeline(response, createWriteStream(outputLocationPath));
1539
+ return { file: outputLocationPath, duration: formatTime(Date.now() - startTime) };
1508
1540
  };
1509
1541
 
1510
1542
  // src/downloadLatestArtifacts.ts
1511
1543
  var downloadLatestArtifacts = async (options) => {
1512
1544
  saveOptions(options);
1513
1545
  if (!isEnvValid()) return process.exit(1);
1514
- const latestBuild = await oraPromise(getLatestBuild(options), {
1546
+ const latestBuild = await oraPromise2(getLatestBuild(), {
1515
1547
  text: "fetching latest build",
1516
1548
  failText: (error) => `fetching latest build failed with error: ${error}`
1517
1549
  });
1518
- if (!latestBuild.slug) {
1519
- console.log("Latest build contained no Slug");
1520
- return process.exit(1);
1521
- }
1522
- const artifactMetadata = await oraPromise(fetchArtifactByType(latestBuild.slug), {
1550
+ if (!latestBuild.slug) throw Error("Latest build contained no Slug");
1551
+ const artifactMetadata = await oraPromise2(fetchArtifactByType(latestBuild.slug), {
1523
1552
  text: "fetching artifact list",
1524
1553
  failText: (error) => `fetching artifact list failed with error: ${error}`
1525
1554
  });
1526
- if (options.debug) debugLog("artifactMetadata", artifactMetadata);
1527
- if (!artifactMetadata || !artifactMetadata.slug) return process.exit(1);
1528
- const artifact = await oraPromise(
1529
- fetchArtifact({
1530
- artifactSlug: artifactMetadata.slug,
1531
- buildSlug: latestBuild.slug
1532
- }),
1555
+ if (!artifactMetadata.slug || !artifactMetadata.title) throw Error("Artifact was Invalid");
1556
+ const artifact = await oraPromise2(
1557
+ fetchArtifact({ artifactSlug: artifactMetadata.slug, buildSlug: latestBuild.slug }),
1533
1558
  { text: "fetching artifact metadata" }
1534
1559
  );
1535
- if (options.debug) debugLog("artifact", artifact);
1536
- if (!artifact || !artifact.expiringDownloadUrl || !artifactMetadata.title) {
1537
- console.log("Artifact is missing data");
1538
- return process.exit(1);
1539
- }
1560
+ if (!artifact.expiringDownloadUrl) throw Error("Artifact is missing expiringDownloadUrl");
1540
1561
  const fileSize = formatBytes(artifact.fileSizeBytes ?? 0);
1541
- await oraPromise(downloadFile(artifact.expiringDownloadUrl, artifactMetadata.title), {
1542
- text: `downloading apk file (${fileSize})`
1562
+ const { file } = await oraPromise2(downloadFile(artifact.expiringDownloadUrl, artifactMetadata.title), {
1563
+ text: `downloading apk file (${fileSize})`,
1564
+ successText: ({ duration }) => `downloading apk file took ${duration}`
1543
1565
  });
1566
+ return file;
1544
1567
  };
1545
1568
 
1546
- // src/bin.ts
1547
- var program = new Command();
1548
- program.version("0.0.4");
1549
- program.command("download").option("-t, --token <TOKEN>", "bitrise api token (or $BITRISE_API_CLI_TOKEN)", process.env.BITRISE_API_CLI_TOKEN).option(
1550
- "-w, --workflow <WORKFLOW_NAME>",
1551
- "workflow to get apk from (or $BITRISE_API_CLI_WORKFLOW)",
1552
- process.env.BITRISE_API_CLI_WORKFLOW
1553
- ).option(
1554
- "-s, --slug <APP_SLUG>",
1555
- "bitrise app slug (or $BITRISE_APP_SLUG) see bitrise project url to get slug",
1556
- process.env.BITRISE_APP_SLUG
1557
- ).option("-a, --artifact <ARTIFACT_TYPE>", 'bitrise artifact type "ios-ipa" or "android-apk"', "android-apk").option("-d, --debug", "output extra debugging", false).description("Download bitrise artifacts").action((options) => downloadLatestArtifacts(options).catch(() => process.exit(1)));
1558
- program.parse(process.argv);
1569
+ // src/cli/commands/download/impl.ts
1570
+ async function impl_default(flags) {
1571
+ if (flags.debug) console.log(JSON.stringify(flags, null, 2));
1572
+ const file = await downloadLatestArtifacts(flags);
1573
+ if (flags.install) {
1574
+ await installApp({ file, flags });
1575
+ }
1576
+ }
1577
+ export {
1578
+ impl_default as default
1579
+ };
package/package.json CHANGED
@@ -1,19 +1,20 @@
1
1
  {
2
2
  "name": "bitrise-api-cli",
3
- "version": "0.0.4",
3
+ "version": "0.0.7",
4
4
  "description": "bitrise api CLI wrapper",
5
5
  "module": "index",
6
6
  "type": "module",
7
7
  "bin": {
8
- "bitrise-api-cli": "./dist/bin.js"
8
+ "bitrise-api-cli": "./dist/cli.js",
9
+ "__bitrise-api-cli_bash_complete": "dist/bash-complete.js"
9
10
  },
10
- "keywords": ["bitrise", "bitriseio"],
11
+ "keywords": ["bitrise", "bitriseio", "apk", "downloader"],
11
12
  "license": "MIT",
12
- "main": "dist/bin.js",
13
+ "main": "dist/cli.js",
13
14
  "files": ["dist"],
14
15
  "scripts": {
15
16
  "build": "tsup",
16
- "build:dev": "tsup --watch",
17
+ "build:dev": "tsup --watch --clean false",
17
18
  "lint": "bunx biome check --fix",
18
19
  "lint:CI": "bunx biome check",
19
20
  "typecheck": "tsc",
@@ -22,14 +23,17 @@
22
23
  "prepublishOnly": "bun i && bun run lint:CI && tsc && bun run build"
23
24
  },
24
25
  "dependencies": {
25
- "commander": "13.1.0",
26
- "ora": "8.2.0"
26
+ "@stricli/auto-complete": "1.1.1",
27
+ "@stricli/core": "1.1.1",
28
+ "ora": "8.2.0",
29
+ "teen_process": "2.2.3"
27
30
  },
28
31
  "devDependencies": {
29
32
  "@biomejs/biome": "1.9.4",
30
33
  "@openapitools/openapi-generator-cli": "2.16.3",
31
34
  "@types/bun": "latest",
32
- "tsup": "8.3.5",
35
+ "@types/teen_process": "^2.0.4",
36
+ "tsup": "8.4.0",
33
37
  "typescript": "5.7.3"
34
38
  }
35
39
  }