heroku 11.9.0 → 11.10.0-beta.0
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/CHANGELOG.md +30 -0
- package/dist/commands/access/add.js +3 -3
- package/dist/commands/access/index.js +6 -6
- package/dist/commands/access/update.js +1 -2
- package/dist/commands/autocomplete/options.js +2 -2
- package/dist/commands/buildpacks/add.d.ts +1 -0
- package/dist/commands/buildpacks/add.js +8 -1
- package/dist/commands/buildpacks/clear.d.ts +1 -0
- package/dist/commands/buildpacks/clear.js +2 -1
- package/dist/commands/buildpacks/index.d.ts +1 -0
- package/dist/commands/buildpacks/index.js +5 -1
- package/dist/commands/buildpacks/info.d.ts +3 -0
- package/dist/commands/buildpacks/info.js +12 -4
- package/dist/commands/buildpacks/remove.d.ts +1 -0
- package/dist/commands/buildpacks/remove.js +9 -2
- package/dist/commands/buildpacks/set.d.ts +1 -0
- package/dist/commands/buildpacks/set.js +8 -1
- package/dist/commands/ci/rerun.js +4 -0
- package/dist/commands/ci/run.js +3 -0
- package/dist/commands/container/push.d.ts +1 -0
- package/dist/commands/container/push.js +3 -1
- package/dist/commands/data/pg/psql.d.ts +0 -2
- package/dist/commands/data/pg/psql.js +11 -43
- package/dist/commands/pg/promote.d.ts +1 -0
- package/dist/commands/pg/promote.js +3 -2
- package/dist/commands/pg/psql.d.ts +1 -0
- package/dist/commands/pg/psql.js +29 -4
- package/dist/commands/redis/promote.d.ts +1 -0
- package/dist/commands/redis/promote.js +7 -5
- package/dist/commands/reviewapps/create.d.ts +13 -0
- package/dist/commands/reviewapps/create.js +64 -0
- package/dist/commands/reviewapps/wait.d.ts +13 -0
- package/dist/commands/reviewapps/wait.js +38 -0
- package/dist/lib/buildpacks/buildpacks.d.ts +1 -1
- package/dist/lib/buildpacks/buildpacks.js +6 -1
- package/dist/lib/ci/pipelines.d.ts +6 -0
- package/dist/lib/ci/pipelines.js +17 -3
- package/dist/lib/container/docker-helper.d.ts +2 -1
- package/dist/lib/container/docker-helper.js +3 -1
- package/dist/lib/pg/one-off-dyno.d.ts +11 -0
- package/dist/lib/pg/one-off-dyno.js +40 -0
- package/dist/lib/reviewapps/create-review-app.d.ts +6 -0
- package/dist/lib/reviewapps/create-review-app.js +67 -0
- package/dist/lib/reviewapps/wait-review-app.d.ts +5 -0
- package/dist/lib/reviewapps/wait-review-app.js +47 -0
- package/oclif.manifest.json +7130 -6703
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,36 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
6
|
|
|
7
|
+
## [11.10.0-beta.0](https://github.com/heroku/cli/compare/v11.9.0...v11.10.0-beta.0) (2026-08-18)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
### Features
|
|
11
|
+
|
|
12
|
+
* add --json flag to heroku buildpacks and subcommands (W-23597909, [#1637](https://github.com/heroku/cli/issues/1637)) ([#3842](https://github.com/heroku/cli/issues/3842)) ([6f56a6f](https://github.com/heroku/cli/commit/6f56a6f6ec38d50d885992a3fdf30d985313e910))
|
|
13
|
+
* add --no-cache flag to container:push (W-23597904, [#1390](https://github.com/heroku/cli/issues/1390)) ([#3840](https://github.com/heroku/cli/issues/3840)) ([91a0c42](https://github.com/heroku/cli/commit/91a0c422a35bd8d7fc58df5d8308bebb70b2f389))
|
|
14
|
+
* add reviewapps:create and reviewapps:wait commands ([#3816](https://github.com/heroku/cli/issues/3816)) ([8ca1582](https://github.com/heroku/cli/commit/8ca1582757aabd950df43531a7f1d3fc579d6a38))
|
|
15
|
+
* **ci:** add not-in-git-repo guard to ci:run and ci:rerun (W-23597902, [#1761](https://github.com/heroku/cli/issues/1761)) ([#3843](https://github.com/heroku/cli/issues/3843)) ([3d21957](https://github.com/heroku/cli/commit/3d2195700420276d61385a6213b7989ae8dc8d1b))
|
|
16
|
+
* enable psql for classic shield ([#3861](https://github.com/heroku/cli/issues/3861)) ([2f0ced0](https://github.com/heroku/cli/commit/2f0ced0daa3d9aada870bd5ac679f7e3663c2dc4))
|
|
17
|
+
* infer --pipeline from heroku.remote git config in CI commands (W-23597910, [#1318](https://github.com/heroku/cli/issues/1318)) ([#3845](https://github.com/heroku/cli/issues/3845)) ([4a0a9b4](https://github.com/heroku/cli/commit/4a0a9b40ec51bf90b2dc11d76c32315aa2e7753f))
|
|
18
|
+
* **pg,redis:** add --as flag to pg:promote and redis:promote (W-23597911, [#1819](https://github.com/heroku/cli/issues/1819)) ([#3841](https://github.com/heroku/cli/issues/3841)) ([8271d51](https://github.com/heroku/cli/commit/8271d5128b214bddafa8a04059496fa0074facc2))
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
### Bug Fixes
|
|
22
|
+
|
|
23
|
+
* flag value autocompletion (PR [#3686](https://github.com/heroku/cli/issues/3686) recreation) ([#3866](https://github.com/heroku/cli/issues/3866)) ([30ac7e2](https://github.com/heroku/cli/commit/30ac7e22c00c53c99b1996bb5ab8cdab0f2e4aa0))
|
|
24
|
+
* update `access` commands (list, update, add) to Utilize Permission Chain Directly. ([#3856](https://github.com/heroku/cli/issues/3856)) ([be14b93](https://github.com/heroku/cli/commit/be14b9365eaba2058fd94f41891399a0c758347e))
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
### Miscellaneous Chores
|
|
28
|
+
|
|
29
|
+
* **deps:** bump actions/download-artifact from 7 to 8 ([#3549](https://github.com/heroku/cli/issues/3549)) ([f951097](https://github.com/heroku/cli/commit/f951097e96fc10a50c7b5438ea158462ad5b0bfc))
|
|
30
|
+
* **deps:** bump actions/upload-artifact from 6 to 7 ([#3550](https://github.com/heroku/cli/issues/3550)) ([427d743](https://github.com/heroku/cli/commit/427d743f20f83705dd75c5948a799288e870255e))
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
### Code Refactoring
|
|
34
|
+
|
|
35
|
+
* remove useless step in Ubuntu install script (PR [#2020](https://github.com/heroku/cli/issues/2020) recreation) ([#3865](https://github.com/heroku/cli/issues/3865)) ([c919b30](https://github.com/heroku/cli/commit/c919b30d36e3c47a194c99fb1c79e60588bfb186))
|
|
36
|
+
|
|
7
37
|
## [11.9.0](https://github.com/heroku/cli/compare/v11.8.1...v11.9.0) (2026-08-04)
|
|
8
38
|
|
|
9
39
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Command, flags } from '@heroku-cli/command';
|
|
2
2
|
import * as color from '@heroku/heroku-cli-util/color';
|
|
3
3
|
import { Args, ux } from '@oclif/core';
|
|
4
|
-
import { getOwner
|
|
4
|
+
import { getOwner } from '../../lib/team-utils.js';
|
|
5
5
|
export default class AccessAdd extends Command {
|
|
6
6
|
static args = {
|
|
7
7
|
email: Args.string({ description: 'email address of the team member', required: true }),
|
|
@@ -23,8 +23,8 @@ export default class AccessAdd extends Command {
|
|
|
23
23
|
const { body: appInfo } = await this.heroku.get(`/apps/${appName}`);
|
|
24
24
|
let output = `Adding ${color.user(email)} access to the app ${color.app(appName)}`;
|
|
25
25
|
let teamFeatures = [];
|
|
26
|
-
if (
|
|
27
|
-
const teamName = getOwner(appInfo?.owner?.email);
|
|
26
|
+
if (appInfo?.team) {
|
|
27
|
+
const teamName = appInfo.team.name ?? getOwner(appInfo?.owner?.email);
|
|
28
28
|
const teamFeaturesRequest = await this.heroku.get(`/teams/${teamName}/features`);
|
|
29
29
|
teamFeatures = teamFeaturesRequest.body;
|
|
30
30
|
}
|
|
@@ -3,7 +3,7 @@ import { HerokuAPIError } from '@heroku-cli/command/lib/api-client.js';
|
|
|
3
3
|
import { color, hux } from '@heroku/heroku-cli-util';
|
|
4
4
|
import { ux } from '@oclif/core/ux';
|
|
5
5
|
import { lazyModuleLoader } from '../../lib/lazy-module-loader.js';
|
|
6
|
-
import { getOwner
|
|
6
|
+
import { getOwner } from '../../lib/team-utils.js';
|
|
7
7
|
export default class AccessIndex extends Command {
|
|
8
8
|
static description = 'list who has access to an app';
|
|
9
9
|
static flags = {
|
|
@@ -18,8 +18,8 @@ export default class AccessIndex extends Command {
|
|
|
18
18
|
const { app: appName, json } = flags;
|
|
19
19
|
const { body: app } = await this.heroku.get(`/apps/${appName}`);
|
|
20
20
|
let { body: collaborators } = await this.heroku.get(`/apps/${appName}/collaborators`);
|
|
21
|
-
if (
|
|
22
|
-
const teamName = getOwner(app.owner?.email);
|
|
21
|
+
if (app.team) {
|
|
22
|
+
const teamName = app.team.name ?? getOwner(app.owner?.email);
|
|
23
23
|
try {
|
|
24
24
|
const { body: members } = await this.heroku.get(`/teams/${teamName}/members`);
|
|
25
25
|
let admins = members.filter(member => member.role === 'admin');
|
|
@@ -39,7 +39,7 @@ export default class AccessIndex extends Command {
|
|
|
39
39
|
if (json)
|
|
40
40
|
printJSON(collaborators);
|
|
41
41
|
else
|
|
42
|
-
printAccess(
|
|
42
|
+
printAccess(collaborators, _);
|
|
43
43
|
}
|
|
44
44
|
}
|
|
45
45
|
function buildCollaboratorsArray(collaboratorsRaw, admins, _) {
|
|
@@ -63,8 +63,8 @@ function buildTableColumns(showPermissions) {
|
|
|
63
63
|
}
|
|
64
64
|
return baseColumns;
|
|
65
65
|
}
|
|
66
|
-
function printAccess(
|
|
67
|
-
const showPermissions =
|
|
66
|
+
function printAccess(collaborators, _) {
|
|
67
|
+
const showPermissions = collaborators.some((c) => c.permissions !== undefined);
|
|
68
68
|
collaborators = _.chain(collaborators)
|
|
69
69
|
.sortBy((c) => c.email || c.user.email)
|
|
70
70
|
.reject((c) => /herokumanager\.com$/.test(c.user.email))
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { Command, flags } from '@heroku-cli/command';
|
|
2
2
|
import * as color from '@heroku/heroku-cli-util/color';
|
|
3
3
|
import { Args, ux } from '@oclif/core';
|
|
4
|
-
import { isTeamApp } from '../../lib/team-utils.js';
|
|
5
4
|
export default class Update extends Command {
|
|
6
5
|
static args = {
|
|
7
6
|
email: Args.string({ description: 'email address of the team member', required: true }),
|
|
@@ -22,7 +21,7 @@ export default class Update extends Command {
|
|
|
22
21
|
const appName = flags.app;
|
|
23
22
|
let permissions = flags.permissions.split(',');
|
|
24
23
|
const { body: appInfo } = await this.heroku.get(`/apps/${appName}`);
|
|
25
|
-
if (!
|
|
24
|
+
if (!appInfo?.team)
|
|
26
25
|
this.error(`Error: cannot update permissions. The app ${color.app(appName)} is not owned by a team`);
|
|
27
26
|
permissions.push('view');
|
|
28
27
|
permissions = [...new Set(permissions.sort())];
|
|
@@ -118,7 +118,7 @@ export default class Options extends AutocompleteBase {
|
|
|
118
118
|
// variable arg (strict: false)
|
|
119
119
|
if (!Klass.strict) {
|
|
120
120
|
cacheKey = cmdArgs[0] && cmdArgs[0].name.toLowerCase();
|
|
121
|
-
cacheCompletion = this.findCompletion(
|
|
121
|
+
cacheCompletion = this.findCompletion(id, cacheKey);
|
|
122
122
|
if (!cacheCompletion)
|
|
123
123
|
this.throwError(`Cannot complete variable arg position for ${id}`);
|
|
124
124
|
}
|
|
@@ -132,7 +132,7 @@ export default class Options extends AutocompleteBase {
|
|
|
132
132
|
}
|
|
133
133
|
// try to auto-populate the completion object
|
|
134
134
|
if (!cacheCompletion) {
|
|
135
|
-
cacheCompletion = this.findCompletion(
|
|
135
|
+
cacheCompletion = this.findCompletion(id, cacheKey);
|
|
136
136
|
}
|
|
137
137
|
return { cacheCompletion, cacheKey };
|
|
138
138
|
}
|
|
@@ -7,6 +7,7 @@ export default class Add extends Command {
|
|
|
7
7
|
static flags: {
|
|
8
8
|
app: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
9
9
|
index: import("@oclif/core/interfaces").OptionFlag<number | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
|
+
json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
10
11
|
remote: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
12
|
};
|
|
12
13
|
run(): Promise<void>;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Command, flags as Flags } from '@heroku-cli/command';
|
|
2
|
+
import { hux } from '@heroku/heroku-cli-util';
|
|
2
3
|
import { Args } from '@oclif/core';
|
|
3
4
|
import { BuildpackCommand } from '../../lib/buildpacks/buildpacks.js';
|
|
4
5
|
export default class Add extends Command {
|
|
@@ -15,6 +16,7 @@ export default class Add extends Command {
|
|
|
15
16
|
char: 'i',
|
|
16
17
|
description: 'the 1-based index of the URL in the list of URLs',
|
|
17
18
|
}),
|
|
19
|
+
json: Flags.boolean({ char: 'j', description: 'output in json format' }),
|
|
18
20
|
remote: Flags.remote(),
|
|
19
21
|
};
|
|
20
22
|
async run() {
|
|
@@ -35,6 +37,11 @@ export default class Add extends Command {
|
|
|
35
37
|
spliceIndex = (foundIndex === -1) ? buildpacks.length : foundIndex;
|
|
36
38
|
}
|
|
37
39
|
const buildpackUpdates = await buildpackCommand.mutate(flags.app, buildpacks, spliceIndex, args.buildpack, 'add');
|
|
38
|
-
|
|
40
|
+
if (flags.json) {
|
|
41
|
+
hux.styledJSON(buildpackUpdates);
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
buildpackCommand.displayUpdate(flags.app, flags.remote || '', buildpackUpdates, 'added');
|
|
45
|
+
}
|
|
39
46
|
}
|
|
40
47
|
}
|
|
@@ -3,6 +3,7 @@ export default class Clear extends Command {
|
|
|
3
3
|
static description: string;
|
|
4
4
|
static flags: {
|
|
5
5
|
app: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
6
|
+
json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
6
7
|
remote: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
7
8
|
};
|
|
8
9
|
run(): Promise<void>;
|
|
@@ -4,11 +4,12 @@ export default class Clear extends Command {
|
|
|
4
4
|
static description = 'clear all buildpacks set on the app';
|
|
5
5
|
static flags = {
|
|
6
6
|
app: Flags.app({ required: true }),
|
|
7
|
+
json: Flags.boolean({ char: 'j', description: 'output in json format' }),
|
|
7
8
|
remote: Flags.remote(),
|
|
8
9
|
};
|
|
9
10
|
async run() {
|
|
10
11
|
const { flags } = await this.parse(Clear);
|
|
11
12
|
const buildpackCommand = new BuildpackCommand(this.heroku);
|
|
12
|
-
await buildpackCommand.clear(flags.app, 'clear', 'cleared');
|
|
13
|
+
await buildpackCommand.clear(flags.app, 'clear', 'cleared', flags.json);
|
|
13
14
|
}
|
|
14
15
|
}
|
|
@@ -3,6 +3,7 @@ export default class Index extends Command {
|
|
|
3
3
|
static description: string;
|
|
4
4
|
static flags: {
|
|
5
5
|
app: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
6
|
+
json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
6
7
|
remote: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
7
8
|
};
|
|
8
9
|
run(): Promise<void>;
|
|
@@ -6,6 +6,7 @@ export default class Index extends Command {
|
|
|
6
6
|
static description = 'list the buildpacks on an app';
|
|
7
7
|
static flags = {
|
|
8
8
|
app: Flags.app({ required: true }),
|
|
9
|
+
json: Flags.boolean({ char: 'j', description: 'output in json format' }),
|
|
9
10
|
remote: Flags.remote(),
|
|
10
11
|
};
|
|
11
12
|
async run() {
|
|
@@ -18,7 +19,10 @@ export default class Index extends Command {
|
|
|
18
19
|
});
|
|
19
20
|
const isFirApp = getGeneration(app) === 'fir';
|
|
20
21
|
const buildpacks = await buildpacksCommand.fetch(flags.app, isFirApp);
|
|
21
|
-
if (
|
|
22
|
+
if (flags.json) {
|
|
23
|
+
hux.styledJSON(buildpacks);
|
|
24
|
+
}
|
|
25
|
+
else if (buildpacks.length === 0) {
|
|
22
26
|
this.log(`${color.app(flags.app)} has no Buildpacks.`);
|
|
23
27
|
}
|
|
24
28
|
else {
|
|
@@ -4,5 +4,8 @@ export default class Info extends Command {
|
|
|
4
4
|
buildpack: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
5
5
|
};
|
|
6
6
|
static description: string;
|
|
7
|
+
static flags: {
|
|
8
|
+
json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
9
|
+
};
|
|
7
10
|
run(): Promise<void>;
|
|
8
11
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Command } from '@heroku-cli/command';
|
|
1
|
+
import { Command, flags as Flags } from '@heroku-cli/command';
|
|
2
2
|
import { BuildpackRegistry } from '@heroku/buildpack-registry';
|
|
3
3
|
import { hux } from '@heroku/heroku-cli-util';
|
|
4
4
|
import { Args, ux } from '@oclif/core';
|
|
@@ -11,8 +11,11 @@ export default class Info extends Command {
|
|
|
11
11
|
}),
|
|
12
12
|
};
|
|
13
13
|
static description = 'fetch info about a buildpack';
|
|
14
|
+
static flags = {
|
|
15
|
+
json: Flags.boolean({ char: 'j', description: 'output in json format' }),
|
|
16
|
+
};
|
|
14
17
|
async run() {
|
|
15
|
-
const { args } = await this.parse(Info);
|
|
18
|
+
const { args, flags } = await this.parse(Info);
|
|
16
19
|
const registry = new BuildpackRegistry();
|
|
17
20
|
const validationResult = BuildpackRegistry.isValidBuildpackSlug(args.buildpack);
|
|
18
21
|
if (!validationResult.isOk) {
|
|
@@ -29,8 +32,13 @@ export default class Info extends Command {
|
|
|
29
32
|
}
|
|
30
33
|
},
|
|
31
34
|
Ok(buildpack) {
|
|
32
|
-
|
|
33
|
-
|
|
35
|
+
if (flags.json) {
|
|
36
|
+
hux.styledJSON(buildpack);
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
hux.styledHeader(args.buildpack);
|
|
40
|
+
hux.styledObject(buildpack, ['description', 'category', 'license', 'support', 'source', 'readme']);
|
|
41
|
+
}
|
|
34
42
|
},
|
|
35
43
|
}, result);
|
|
36
44
|
}
|
|
@@ -7,6 +7,7 @@ export default class Remove extends Command {
|
|
|
7
7
|
static flags: {
|
|
8
8
|
app: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
9
9
|
index: import("@oclif/core/interfaces").OptionFlag<number | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
|
+
json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
10
11
|
remote: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
12
|
};
|
|
12
13
|
run(): Promise<void>;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Command, flags as Flags } from '@heroku-cli/command';
|
|
2
|
+
import { hux } from '@heroku/heroku-cli-util';
|
|
2
3
|
import * as color from '@heroku/heroku-cli-util/color';
|
|
3
4
|
import { Args, ux } from '@oclif/core';
|
|
4
5
|
import { BuildpackCommand } from '../../lib/buildpacks/buildpacks.js';
|
|
@@ -15,6 +16,7 @@ export default class Remove extends Command {
|
|
|
15
16
|
char: 'i',
|
|
16
17
|
description: 'the 1-based index of the URL to remove from the list of URLs',
|
|
17
18
|
}),
|
|
19
|
+
json: Flags.boolean({ char: 'j', description: 'output in json format' }),
|
|
18
20
|
remote: Flags.remote(),
|
|
19
21
|
};
|
|
20
22
|
async run() {
|
|
@@ -43,11 +45,16 @@ export default class Remove extends Command {
|
|
|
43
45
|
ux.error('Buildpack not found. Nothing was removed.', { exit: 1 });
|
|
44
46
|
}
|
|
45
47
|
if (buildpacks.length === 1) {
|
|
46
|
-
await buildpackCommand.clear(flags.app, 'remove', 'removed');
|
|
48
|
+
await buildpackCommand.clear(flags.app, 'remove', 'removed', flags.json);
|
|
47
49
|
}
|
|
48
50
|
else {
|
|
49
51
|
const buildpackUpdates = await buildpackCommand.mutate(flags.app, buildpacks, spliceIndex, args.buildpack, 'remove');
|
|
50
|
-
|
|
52
|
+
if (flags.json) {
|
|
53
|
+
hux.styledJSON(buildpackUpdates);
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
buildpackCommand.displayUpdate(flags.app, flags.remote || '', buildpackUpdates, 'removed');
|
|
57
|
+
}
|
|
51
58
|
}
|
|
52
59
|
}
|
|
53
60
|
}
|
|
@@ -7,6 +7,7 @@ export default class Set extends Command {
|
|
|
7
7
|
static flags: {
|
|
8
8
|
app: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
9
9
|
index: import("@oclif/core/interfaces").OptionFlag<number | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
|
+
json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
10
11
|
remote: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
12
|
};
|
|
12
13
|
run(): Promise<void>;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Command, flags as Flags } from '@heroku-cli/command';
|
|
2
|
+
import { hux } from '@heroku/heroku-cli-util';
|
|
2
3
|
import { Args } from '@oclif/core';
|
|
3
4
|
import { BuildpackCommand } from '../../lib/buildpacks/buildpacks.js';
|
|
4
5
|
export default class Set extends Command {
|
|
@@ -15,6 +16,7 @@ export default class Set extends Command {
|
|
|
15
16
|
char: 'i',
|
|
16
17
|
description: 'the 1-based index of the URL in the list of URLs',
|
|
17
18
|
}),
|
|
19
|
+
json: Flags.boolean({ char: 'j', description: 'output in json format' }),
|
|
18
20
|
remote: Flags.remote(),
|
|
19
21
|
};
|
|
20
22
|
async run() {
|
|
@@ -35,6 +37,11 @@ export default class Set extends Command {
|
|
|
35
37
|
spliceIndex = (foundIndex === -1) ? buildpacks.length : foundIndex;
|
|
36
38
|
}
|
|
37
39
|
const buildpackUpdates = await buildpackCommand.mutate(flags.app, buildpacks, spliceIndex, args.buildpack, 'set');
|
|
38
|
-
|
|
40
|
+
if (flags.json) {
|
|
41
|
+
hux.styledJSON(buildpackUpdates);
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
buildpackCommand.displayUpdate(flags.app, flags.remote || '', buildpackUpdates, 'set');
|
|
45
|
+
}
|
|
39
46
|
}
|
|
40
47
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Command, flags } from '@heroku-cli/command';
|
|
2
2
|
import * as color from '@heroku/heroku-cli-util/color';
|
|
3
3
|
import { Args, ux } from '@oclif/core';
|
|
4
|
+
import { gitService } from '../../lib/ci/git.js';
|
|
4
5
|
import { getPipeline } from '../../lib/ci/pipelines.js';
|
|
5
6
|
import { createSourceBlob } from '../../lib/ci/source.js';
|
|
6
7
|
import { displayAndExit } from '../../lib/ci/test-run.js';
|
|
@@ -20,6 +21,9 @@ export default class CiReRun extends Command {
|
|
|
20
21
|
};
|
|
21
22
|
async run() {
|
|
22
23
|
const { args, flags } = await this.parse(CiReRun);
|
|
24
|
+
if (!gitService.inGitRepo()) {
|
|
25
|
+
this.error(`You can't run ${color.command('heroku ci:rerun')} outside of your app's git repository.`);
|
|
26
|
+
}
|
|
23
27
|
const pipeline = await getPipeline(flags, this.heroku);
|
|
24
28
|
let sourceTestRun;
|
|
25
29
|
if (args.number) {
|
package/dist/commands/ci/run.js
CHANGED
|
@@ -18,6 +18,9 @@ export default class CiRun extends Command {
|
|
|
18
18
|
};
|
|
19
19
|
async run() {
|
|
20
20
|
const { flags } = await this.parse(CiRun);
|
|
21
|
+
if (!gitService.inGitRepo()) {
|
|
22
|
+
this.error(`You can't run ${color.command('heroku ci:run')} outside of your app's git repository.`);
|
|
23
|
+
}
|
|
21
24
|
const pipeline = await getPipeline(flags, this.heroku);
|
|
22
25
|
const commit = await gitService.readCommit('HEAD');
|
|
23
26
|
ux.action.start('Preparing source');
|
|
@@ -7,6 +7,7 @@ export default class Push extends Command {
|
|
|
7
7
|
app: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
8
8
|
arg: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
9
9
|
'context-path': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
|
+
'no-cache': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
10
11
|
recursive: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
11
12
|
remote: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
13
|
verbose: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
@@ -18,6 +18,7 @@ export default class Push extends Command {
|
|
|
18
18
|
app: flags.app({ required: true }),
|
|
19
19
|
arg: flags.string({ description: 'set build-time variables' }),
|
|
20
20
|
'context-path': flags.string({ description: 'path to use as build context (defaults to Dockerfile dir)' }),
|
|
21
|
+
'no-cache': flags.boolean({ description: 'don\'t use cache when building the image' }),
|
|
21
22
|
recursive: flags.boolean({ char: 'R', description: 'pushes Dockerfile.<process> found in current and subdirectories' }),
|
|
22
23
|
remote: flags.remote({ char: 'r' }),
|
|
23
24
|
verbose: flags.boolean({ char: 'v' }),
|
|
@@ -27,7 +28,7 @@ export default class Push extends Command {
|
|
|
27
28
|
dockerHelper = new DockerHelper();
|
|
28
29
|
async run() {
|
|
29
30
|
const { argv: processTypes, flags } = await this.parse(Push);
|
|
30
|
-
const { app, arg, 'context-path': contextPath, recursive, verbose } = flags;
|
|
31
|
+
const { app, arg, 'context-path': contextPath, 'no-cache': noCache, recursive, verbose } = flags;
|
|
31
32
|
if (verbose) {
|
|
32
33
|
debug.enabled = true;
|
|
33
34
|
}
|
|
@@ -59,6 +60,7 @@ export default class Push extends Command {
|
|
|
59
60
|
arch: this.config.arch,
|
|
60
61
|
buildArgs,
|
|
61
62
|
dockerfile: job.dockerfile,
|
|
63
|
+
noCache,
|
|
62
64
|
path: contextPath,
|
|
63
65
|
resource: job.resource,
|
|
64
66
|
});
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { Command } from '@heroku-cli/command';
|
|
2
|
-
import { DynoOpts } from '../../../lib/run/dyno.js';
|
|
3
2
|
export default class DataPgPsql extends Command {
|
|
4
3
|
static args: {
|
|
5
4
|
database: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
@@ -14,5 +13,4 @@ export default class DataPgPsql extends Command {
|
|
|
14
13
|
remote: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
15
14
|
};
|
|
16
15
|
run(): Promise<void>;
|
|
17
|
-
runThroughOneOffDyno(opts: DynoOpts): Promise<void>;
|
|
18
16
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { Command, flags as Flags } from '@heroku-cli/command';
|
|
2
2
|
import { color, utils } from '@heroku/heroku-cli-util';
|
|
3
|
-
import { Args
|
|
3
|
+
import { Args } from '@oclif/core';
|
|
4
4
|
import tsheredoc from 'tsheredoc';
|
|
5
|
-
import
|
|
5
|
+
import { runPsqlThroughOneOffDyno } from '../../../lib/pg/one-off-dyno.js';
|
|
6
6
|
const heredoc = tsheredoc.default;
|
|
7
7
|
export default class DataPgPsql extends Command {
|
|
8
8
|
static args = {
|
|
@@ -38,35 +38,18 @@ export default class DataPgPsql extends Command {
|
|
|
38
38
|
async run() {
|
|
39
39
|
const { args, flags } = await this.parse(DataPgPsql);
|
|
40
40
|
const { database: databaseArg } = args;
|
|
41
|
-
const {
|
|
41
|
+
const { 'channel-binding': channelBinding, command, file } = flags;
|
|
42
42
|
const dbResolver = new utils.pg.DatabaseResolver(this.heroku);
|
|
43
|
-
const db = await dbResolver.getDatabase(app, databaseArg);
|
|
44
|
-
if (utils.pg.
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
+ `--set channel_binding=${channelBinding} $${db.attachment.name}_URL`;
|
|
51
|
-
}
|
|
52
|
-
else {
|
|
53
|
-
const prompt = `${db.attachment.app.name}::${db.attachment.name}%R%# `;
|
|
54
|
-
psqlCommand = `psql --set PROMPT1="${prompt}" --set PROMPT2="${prompt}" --set sslmode=require `
|
|
55
|
-
+ `--set channel_binding=${channelBinding} $${db.attachment.name}_URL`;
|
|
56
|
-
}
|
|
57
|
-
const opts = {
|
|
58
|
-
app,
|
|
59
|
-
attach: true,
|
|
60
|
-
command: psqlCommand,
|
|
61
|
-
env: `PGAPPNAME='psql ${command ? 'non-' : ''}interactive';PGSSLMODE=require;PGCHANNELBINDING=${channelBinding}`,
|
|
62
|
-
'exit-code': true,
|
|
43
|
+
const db = await dbResolver.getDatabase(flags.app, databaseArg);
|
|
44
|
+
if (utils.pg.isPrivateNetworkDatabase(db.attachment.addon)) {
|
|
45
|
+
return runPsqlThroughOneOffDyno({
|
|
46
|
+
channelBinding: channelBinding,
|
|
47
|
+
command,
|
|
48
|
+
db,
|
|
49
|
+
file,
|
|
63
50
|
heroku: this.heroku,
|
|
64
|
-
'no-tty': false,
|
|
65
51
|
notificationSubtitle: 'heroku data:pg:psql',
|
|
66
|
-
|
|
67
|
-
showStatus: false,
|
|
68
|
-
};
|
|
69
|
-
return this.runThroughOneOffDyno(opts);
|
|
52
|
+
});
|
|
70
53
|
}
|
|
71
54
|
const psqlService = new utils.pg.PsqlService(db);
|
|
72
55
|
console.error(`--> Connecting to ${color.yellow(db.attachment.addon.name)}`);
|
|
@@ -85,19 +68,4 @@ export default class DataPgPsql extends Command {
|
|
|
85
68
|
else
|
|
86
69
|
await psqlService.interactiveSession(cmdArgs);
|
|
87
70
|
}
|
|
88
|
-
async runThroughOneOffDyno(opts) {
|
|
89
|
-
const dyno = new Dyno(opts);
|
|
90
|
-
try {
|
|
91
|
-
await dyno.start();
|
|
92
|
-
}
|
|
93
|
-
catch (error) {
|
|
94
|
-
const dynoError = error;
|
|
95
|
-
if (dynoError.exitCode) {
|
|
96
|
-
ux.error(dynoError.message, { code: String(dynoError.exitCode), exit: dynoError.exitCode });
|
|
97
|
-
}
|
|
98
|
-
else {
|
|
99
|
-
throw error;
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
71
|
}
|
|
@@ -6,6 +6,7 @@ export default class Promote extends Command {
|
|
|
6
6
|
static description: string;
|
|
7
7
|
static flags: {
|
|
8
8
|
app: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
9
|
+
as: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
9
10
|
force: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
10
11
|
remote: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
12
|
};
|
|
@@ -13,13 +13,14 @@ export default class Promote extends Command {
|
|
|
13
13
|
static description = 'sets DATABASE as your DATABASE_URL';
|
|
14
14
|
static flags = {
|
|
15
15
|
app: flags.app({ required: true }),
|
|
16
|
+
as: flags.string({ description: 'name for the database attachment' }),
|
|
16
17
|
force: flags.boolean({ char: 'f' }),
|
|
17
18
|
remote: flags.remote(),
|
|
18
19
|
};
|
|
19
20
|
static topic = 'pg';
|
|
20
21
|
async run() {
|
|
21
22
|
const { args, flags } = await this.parse(Promote);
|
|
22
|
-
const { app, force } = flags;
|
|
23
|
+
const { app, as, force } = flags;
|
|
23
24
|
const { database } = args;
|
|
24
25
|
const dbResolver = new utils.pg.DatabaseResolver(this.heroku);
|
|
25
26
|
const attachment = await dbResolver.getAttachment(app, database);
|
|
@@ -79,7 +80,7 @@ export default class Promote extends Command {
|
|
|
79
80
|
addon: { name: attachment.addon.name },
|
|
80
81
|
app: { name: app },
|
|
81
82
|
confirm: app,
|
|
82
|
-
name: 'DATABASE',
|
|
83
|
+
name: as || 'DATABASE',
|
|
83
84
|
namespace: attachment.namespace || null,
|
|
84
85
|
},
|
|
85
86
|
});
|
|
@@ -7,6 +7,7 @@ export default class Psql extends Command {
|
|
|
7
7
|
static description: string;
|
|
8
8
|
static flags: {
|
|
9
9
|
app: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
|
+
'channel-binding': import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
11
|
command: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
12
|
credential: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
13
|
file: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
package/dist/commands/pg/psql.js
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { Command, flags } from '@heroku-cli/command';
|
|
2
2
|
import { color, utils } from '@heroku/heroku-cli-util';
|
|
3
3
|
import { Args } from '@oclif/core';
|
|
4
|
+
import tsheredoc from 'tsheredoc';
|
|
5
|
+
import { runPsqlThroughOneOffDyno } from '../../lib/pg/one-off-dyno.js';
|
|
4
6
|
import { nls } from '../../nls.js';
|
|
7
|
+
const heredoc = tsheredoc.default;
|
|
5
8
|
export default class Psql extends Command {
|
|
6
9
|
static aliases = ['psql'];
|
|
7
10
|
static args = {
|
|
@@ -10,6 +13,14 @@ export default class Psql extends Command {
|
|
|
10
13
|
static description = 'open a psql shell to the database';
|
|
11
14
|
static flags = {
|
|
12
15
|
app: flags.app({ required: true }),
|
|
16
|
+
'channel-binding': flags.string({
|
|
17
|
+
default: 'require',
|
|
18
|
+
description: heredoc('override the default channel binding behavior (required). '
|
|
19
|
+
+ 'Can be "disable" to disable channel binding if you run into compatibility issues with your libpq version '
|
|
20
|
+
+ 'or if it was compiled without SSL support.'),
|
|
21
|
+
hidden: true,
|
|
22
|
+
options: ['disable', 'require'],
|
|
23
|
+
}),
|
|
13
24
|
command: flags.string({ char: 'c', description: 'SQL command to run' }),
|
|
14
25
|
credential: flags.string({ description: 'credential to use' }),
|
|
15
26
|
file: flags.string({ char: 'f', description: 'SQL file to run' }),
|
|
@@ -17,7 +28,7 @@ export default class Psql extends Command {
|
|
|
17
28
|
};
|
|
18
29
|
async run() {
|
|
19
30
|
const { args, flags } = await this.parse(Psql);
|
|
20
|
-
const { app, command, credential, file } = flags;
|
|
31
|
+
const { app, 'channel-binding': channelBinding, command, credential, file } = flags;
|
|
21
32
|
const namespace = credential ? `credential:${credential}` : undefined;
|
|
22
33
|
let db;
|
|
23
34
|
const dbResolver = new utils.pg.DatabaseResolver(this.heroku);
|
|
@@ -30,18 +41,32 @@ export default class Psql extends Command {
|
|
|
30
41
|
}
|
|
31
42
|
throw error;
|
|
32
43
|
}
|
|
44
|
+
if (utils.pg.isPrivateNetworkDatabase(db.attachment.addon)) {
|
|
45
|
+
return runPsqlThroughOneOffDyno({
|
|
46
|
+
channelBinding: channelBinding,
|
|
47
|
+
command,
|
|
48
|
+
db,
|
|
49
|
+
file,
|
|
50
|
+
heroku: this.heroku,
|
|
51
|
+
notificationSubtitle: 'heroku pg:psql',
|
|
52
|
+
});
|
|
53
|
+
}
|
|
33
54
|
const psqlService = new utils.pg.PsqlService(db);
|
|
34
55
|
console.error(`--> Connecting to ${color.datastore(db.attachment.addon.name)}`);
|
|
56
|
+
const cmdArgs = [
|
|
57
|
+
'--set',
|
|
58
|
+
`channel_binding=${channelBinding}`,
|
|
59
|
+
];
|
|
35
60
|
if (command) {
|
|
36
|
-
const output = await psqlService.execQuery(command);
|
|
61
|
+
const output = await psqlService.execQuery(command, cmdArgs);
|
|
37
62
|
process.stdout.write(output);
|
|
38
63
|
}
|
|
39
64
|
else if (file) {
|
|
40
|
-
const output = await psqlService.execFile(file);
|
|
65
|
+
const output = await psqlService.execFile(file, cmdArgs);
|
|
41
66
|
process.stdout.write(output);
|
|
42
67
|
}
|
|
43
68
|
else {
|
|
44
|
-
await psqlService.interactiveSession();
|
|
69
|
+
await psqlService.interactiveSession(cmdArgs);
|
|
45
70
|
}
|
|
46
71
|
}
|
|
47
72
|
}
|
|
@@ -6,6 +6,7 @@ export default class Promote extends Command {
|
|
|
6
6
|
static description: string;
|
|
7
7
|
static flags: {
|
|
8
8
|
app: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
9
|
+
as: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
9
10
|
remote: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
11
|
};
|
|
11
12
|
static topic: string;
|