├── .brev
├── .gitignore
├── nix.sh
├── ports.yaml
└── setup.sh
├── .devcontainer
├── Dockerfile
├── devcontainer.json
└── library-scripts
│ ├── common-debian.sh
│ └── docker-in-docker-debian.sh
├── .direnv
├── flake-profile
├── flake-profile-1-link
├── flake-profile-10-link
├── flake-profile-11-link
├── flake-profile-12-link
├── flake-profile-13-link
├── flake-profile-2-link
├── flake-profile-3-link
├── flake-profile-4-link
├── flake-profile-5-link
├── flake-profile-6-link
├── flake-profile-7-link
├── flake-profile-8-link
└── flake-profile-9-link
├── .gitattributes
├── .github
├── dependabot.yml
└── workflows
│ ├── Test_CanClonePublicRepoWithoutAuthorizeddKeysAllFormats.yml
│ ├── Test_ChangePwd.yml
│ ├── Test_CustomBranchCustomSetupCustomFolder.yml
│ ├── Test_NoProjectBrev.yml
│ ├── Test_NoUserBrevNoProj.yml
│ ├── Test_NoUserBrevProj.yml
│ ├── Test_ProjectRepoNoBrev.yml
│ ├── Test_ProvidedSetupFileChange.yml
│ ├── Test_ProvidedSetupRanNoProj.yml
│ ├── Test_ProvidedSetupUpdated.yml
│ ├── Test_UnauthenticatedSSHKey.yml
│ ├── Test_UserBrevProjectBrevV0.yml
│ ├── Test_UserBrevProjectBrevV1All.yml
│ ├── Test_UserBrevProjectBrevV1Minimal.yml
│ ├── Test_VscodeExtension.yml
│ ├── Test_httpGit.yml
│ ├── build.yml
│ ├── codeql-analysis.yml
│ ├── fmt.yml
│ ├── legacy.yml
│ ├── lint.yml
│ ├── release.yml
│ ├── test.yml
│ └── vet.yml
├── .gitignore
├── .golangci.yml
├── .goreleaser.yml
├── .projectile
├── .vscode
├── extensions.json
├── launch.json
├── settings.json
└── tasks.json
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── Dockerfile
├── LICENSE
├── Makefile
├── README.md
├── assets
├── blank_v0.json
├── std_setup_v0.json
├── test_keypair.json
├── test_noauth_keypair.json
├── test_setup.sh
├── test_setup_v0_norepo.json
├── test_setup_v0_repo.json
└── test_workspace.json
├── bin
├── gen-e2e-actions.py
├── install-gateway-linux.sh
├── install-gateway-mac.sh
├── install-gateway-macm1.sh
├── install-latest-linux.sh
├── install-latest.sh
├── newcmd-v2.sh
├── newcmd.sh
└── remove-queued-jobs.sh
├── docs
└── CONTRIBUTING.md
├── e2etest
└── setup
│ ├── setup.go
│ └── setup_test.go
├── flake-explorations
├── flake-with-both-shell-and-flake.nix
├── flake.lock
├── flake.nix
└── result
├── flake.lock
├── flake.nix
├── go.mod
├── go.sum
├── main.go
├── pkg
├── analytics
│ └── analytics.go
├── auth
│ ├── auth.go
│ ├── auth0.go
│ ├── auth0_test.go
│ ├── auth_test.go
│ └── kas.go
├── autostartconf
│ ├── aptbinary.go
│ ├── autostartconf.go
│ ├── darwin.go
│ ├── linux.go
│ └── staticbinary.go
├── cmd
│ ├── background
│ │ ├── background.go
│ │ └── background_test.go
│ ├── clipboard
│ │ ├── clipboard.go
│ │ └── clipboard_listener.go
│ ├── cmd.go
│ ├── cmderrors
│ │ ├── cmderrors.go
│ │ └── cmderrors_test.go
│ ├── completions
│ │ └── completions.go
│ ├── configureenvvars
│ │ ├── configureenvvars.go
│ │ ├── configureenvvars_test.go
│ │ ├── lex.go
│ │ └── lex_test.go
│ ├── connect
│ │ └── connect.go
│ ├── create
│ │ ├── create.go
│ │ ├── create_test.go
│ │ └── doc.md
│ ├── delete
│ │ ├── delete.go
│ │ ├── delete_test.go
│ │ └── doc.md
│ ├── envvars
│ │ ├── envvars.go
│ │ └── envvars_test.go
│ ├── fu
│ │ ├── fu.go
│ │ └── fu_test.go
│ ├── healthcheck
│ │ └── healthcheck.go
│ ├── hello
│ │ ├── hello.go
│ │ ├── hello_test.go
│ │ ├── onboarding_utils.go
│ │ ├── steps.go
│ │ └── updateUser.go
│ ├── importideconfig
│ │ ├── importideconfig.go
│ │ └── importideconfig_test.go
│ ├── initfile
│ │ └── initfile.go
│ ├── invite
│ │ ├── invite.go
│ │ └── invite_test.go
│ ├── login
│ │ ├── login.go
│ │ └── login_test.go
│ ├── logout
│ │ └── logout.go
│ ├── ls
│ │ ├── ls.go
│ │ └── ls_test.go
│ ├── notebook
│ │ ├── notebook.go
│ │ └── notebook_test.go
│ ├── ollama
│ │ ├── ollama.go
│ │ ├── ollama_test.go
│ │ ├── ollamauiverb.yaml
│ │ └── ollamaverb.yaml
│ ├── open
│ │ ├── open.go
│ │ └── open_test.go
│ ├── org
│ │ ├── ls.go
│ │ ├── org.go
│ │ ├── org_test.go
│ │ └── set.go
│ ├── paths
│ │ ├── paths.go
│ │ └── paths_test.go
│ ├── portforward
│ │ ├── portforward.go
│ │ └── portforward_test.go
│ ├── profile
│ │ ├── profile.go
│ │ └── profile_test.go
│ ├── proxy
│ │ ├── proxy.go
│ │ └── proxy_test.go
│ ├── recreate
│ │ ├── doc.md
│ │ └── recreate.go
│ ├── refresh
│ │ ├── refresh.go
│ │ └── refresh_test.go
│ ├── reset
│ │ ├── doc.md
│ │ ├── reset.go
│ │ └── reset_test.go
│ ├── runtasks
│ │ ├── doc.md
│ │ ├── runtasks.go
│ │ └── runtasks_test.go
│ ├── scale
│ │ └── scale.go
│ ├── secret
│ │ ├── secret.go
│ │ └── secret_test.go
│ ├── set
│ │ ├── set.go
│ │ └── set_test.go
│ ├── setupworkspace
│ │ └── setupworkspace.go
│ ├── shell
│ │ ├── shell.go
│ │ └── shell_test.go
│ ├── sshkeys
│ │ ├── sshkeys.go
│ │ └── sshkeys_test.go
│ ├── start
│ │ ├── doc.md
│ │ ├── start.go
│ │ └── start_test.go
│ ├── status
│ │ ├── doc.md
│ │ ├── status.go
│ │ └── status_test.go
│ ├── stop
│ │ ├── doc.md
│ │ ├── stop.go
│ │ └── stop_test.go
│ ├── tasks
│ │ └── tasks.go
│ ├── test
│ │ ├── test.go
│ │ └── test_test.go
│ ├── updatemodel
│ │ ├── updatemodel.go
│ │ └── updatemodel_test.go
│ ├── util
│ │ └── util.go
│ ├── version
│ │ ├── version.go
│ │ └── version_test.go
│ ├── workspacegroups
│ │ └── workspacegroups.go
│ └── writeconnectionevent
│ │ ├── writeconnectionevent.go
│ │ └── writeconnectionevent_test.go
├── cmdcontext
│ ├── cmdcontext.go
│ └── cmdwriter.go
├── collections
│ └── collections.go
├── config
│ └── config.go
├── entity
│ ├── entity.go
│ ├── generic
│ │ └── entitygeneric.go
│ └── virtualproject
│ │ ├── virtualproject.go
│ │ └── virtualproject_test.go
├── errors
│ ├── errors.go
│ └── errors_test.go
├── featureflag
│ ├── featureflag.go
│ └── featureflag_test.go
├── files
│ ├── files.go
│ └── files_test.go
├── huproxyclient
│ ├── huproxyclient.go
│ └── huproxyclient_test.go
├── ids
│ └── ids.go
├── instancetypes
│ └── instancetypes.go
├── mergeshells
│ ├── mergeshells.go
│ └── templates
│ │ ├── gatsby
│ │ └── gatsby
│ │ ├── golang
│ │ └── golang
│ │ ├── node
│ │ ├── 14
│ │ └── node
│ │ └── rust
│ │ └── rust
├── portforward
│ ├── portforward.go
│ └── portforward_test.go
├── prefixid
│ └── prefixid.go
├── remoteversion
│ └── remoteversion.go
├── setupscript
│ ├── setupscript.go
│ └── setupscript_test.go
├── setupworkspace
│ ├── setupworkspace.go
│ ├── setupworkspace_test.go
│ └── validate.go
├── ssh
│ ├── ssh.go
│ ├── ssh_test.go
│ ├── sshconfigurer.go
│ ├── sshconfigurer_test.go
│ └── tasks.go
├── store
│ ├── authtoken.go
│ ├── authtoken_test.go
│ ├── aws.go
│ ├── basic.go
│ ├── basic_test.go
│ ├── cloudflared.go
│ ├── cloudflared_test.go
│ ├── file.go
│ ├── file_test.go
│ ├── healthcheck.go
│ ├── http.go
│ ├── http_test.go
│ ├── jetbrainsgateway.go
│ ├── organization.go
│ ├── organization_test.go
│ ├── release.go
│ ├── secret.go
│ ├── setupscripturl.go
│ ├── ssh.go
│ ├── ssh_test.go
│ ├── user.go
│ ├── user_test.go
│ ├── workspace.go
│ ├── workspace_test.go
│ └── workspacegroup.go
├── tasks
│ ├── tasks.go
│ └── tasks_test.go
├── terminal
│ ├── display.go
│ └── terminal.go
├── uri
│ └── uri.go
├── util
│ ├── util.go
│ └── util_test.go
├── workspacemanager
│ ├── workspacemanager.go
│ └── workspacemanager_test.go
├── workspacemanagerv2
│ ├── containermanager.go
│ ├── containermanager_test.go
│ ├── volumes.go
│ ├── volumes_test.go
│ ├── workspacemanagerv2.go
│ └── workspacemanagerv2_test.go
└── writeconnectionevent
│ └── writeconnectionevent.go
├── prod_lite.env
├── shell.nix
└── tools
├── go.mod
├── go.sum
└── tools.go
/.brev/.gitignore:
--------------------------------------------------------------------------------
1 | ports.yaml
2 | logs
3 |
--------------------------------------------------------------------------------
/.brev/nix.sh:
--------------------------------------------------------------------------------
1 | # NIX STUFF:
2 | wget https://nixos.org/nix/install -O nix-install
3 | yes | sh nix-install --daemon
4 | sudo sh -c "echo 'build-users-group = nixbld
5 | keep-outputs = true
6 | keep-derivations = true
7 | experimental-features = nix-command flakes
8 | trusted-users = root ubuntu
9 | build-users-group = nixbld
10 | ' > /etc/nix/nix.conf"
--------------------------------------------------------------------------------
/.brev/ports.yaml:
--------------------------------------------------------------------------------
1 | #############################################################################################
2 | ##### Expose your port publicly here so other people or services can use it. #####
3 | ##### #####
4 | ##### ex. You want to connect a frontend to a backend. Run the project you want to #####
5 | ##### expose and add its port here. This service can be accessed at #####
6 | ##### 3000-projectName-user-org.brev.sh. #####
7 | ##### #####
8 | ##### Read more here: https://github.com/brevdev/dotbrev/blob/main/README.md #####
9 | #############################################################################################
10 |
11 | # version: "1.0"
12 | # ports:
13 | # - "3000"
14 | # - "5000"
15 | # - "8000"
16 |
--------------------------------------------------------------------------------
/.devcontainer/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM golang:1.22.6
2 |
3 | # [Option] Install zsh
4 | ARG INSTALL_ZSH="true"
5 | # [Option] Upgrade OS packages to their latest versions
6 | ARG UPGRADE_PACKAGES="false"
7 | # [Option] Enable non-root Docker access in container
8 | ARG ENABLE_NONROOT_DOCKER="true"
9 | # [Option] Use the OSS Moby Engine instead of the licensed Docker Engine
10 | ARG USE_MOBY="true"
11 |
12 | # Install needed packages and setup non-root user. Use a separate RUN statement to add your
13 | # own dependencies. A user of "automatic" attempts to reuse an user ID if one already exists.
14 | ARG USERNAME=automatic
15 | ARG USER_UID=1000
16 | ARG USER_GID=$USER_UID
17 | COPY library-scripts/*.sh /tmp/library-scripts/
18 | RUN apt-get update \
19 | && /bin/bash /tmp/library-scripts/common-debian.sh "${INSTALL_ZSH}" "${USERNAME}" "${USER_UID}" "${USER_GID}" "${UPGRADE_PACKAGES}" \
20 | # Use Docker script from script library to set things up
21 | && /bin/bash /tmp/library-scripts/docker-in-docker-debian.sh "${ENABLE_NONROOT_DOCKER}" "${USERNAME}" "${USE_MOBY}" \
22 | # Clean up
23 | && apt-get autoremove -y && apt-get clean -y && rm -rf /var/lib/apt/lists/* /tmp/library-scripts/
24 |
25 | VOLUME [ "/var/lib/docker" ]
26 |
27 | # Setting the ENTRYPOINT to docker-init.sh will start up the Docker Engine
28 | # inside the container "overrideCommand": false is set in devcontainer.json.
29 | # The script will also execute CMD if you need to alter startup behaviors.
30 | ENTRYPOINT [ "/usr/local/share/docker-init.sh" ]
31 | CMD [ "sleep", "infinity" ]
32 |
33 | # [Optional] Uncomment this section to install additional OS packages.
34 | # RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
35 | # && apt-get -y install --no-install-recommends
36 |
--------------------------------------------------------------------------------
/.devcontainer/devcontainer.json:
--------------------------------------------------------------------------------
1 | // For format details, see https://aka.ms/vscode-remote/devcontainer.json or
2 | // https://github.com/microsoft/vscode-dev-containers
3 | {
4 | "name": "Go with Docker in Docker",
5 | "dockerFile": "Dockerfile",
6 | "runArgs": [
7 | "--init",
8 | "--privileged"
9 | ],
10 | "overrideCommand": false,
11 | // Set *default* container specific settings.json values on container create.
12 | "settings": {
13 | "terminal.integrated.shell.linux": "/bin/zsh",
14 | "go.gopath": "/go"
15 | },
16 | // Add the IDs of extensions you want installed when the container is created.
17 | "extensions": [
18 | "golang.Go",
19 | "ms-azuretools.vscode-docker"
20 | ],
21 | // Use 'forwardPorts' to make a list of ports inside the container available locally.
22 | // "forwardPorts": [],
23 | // Use 'postCreateCommand' to run commands after the container is created.
24 | "postCreateCommand": "apt-get update && apt-get install -y git && make install"
25 | // Uncomment to connect as a non-root user. See https://aka.ms/vscode-remote/containers/non-root.
26 | // "remoteUser": "vscode"
27 | }
--------------------------------------------------------------------------------
/.direnv/flake-profile:
--------------------------------------------------------------------------------
1 | flake-profile-13-link
--------------------------------------------------------------------------------
/.direnv/flake-profile-1-link:
--------------------------------------------------------------------------------
1 | /nix/store/8n92g7fw4nix506iwq8nl1fb6ay2sdgc-nix-shell-env
--------------------------------------------------------------------------------
/.direnv/flake-profile-10-link:
--------------------------------------------------------------------------------
1 | /nix/store/bz0fxgpxhnhd5k6pl94yx5b0cnkihjfm-nix-shell-env
--------------------------------------------------------------------------------
/.direnv/flake-profile-11-link:
--------------------------------------------------------------------------------
1 | /nix/store/vlm7h8szz6zk7pw4kbf6rx6wgzz2xkwp-nix-shell-env
--------------------------------------------------------------------------------
/.direnv/flake-profile-12-link:
--------------------------------------------------------------------------------
1 | /nix/store/mnyy3zgc5kaij16qzkfkh7b228y6drg1-nix-shell-env
--------------------------------------------------------------------------------
/.direnv/flake-profile-13-link:
--------------------------------------------------------------------------------
1 | /nix/store/kx2kx5d0fbjdrcmpw8aq3ldv8c1rr3v0-nix-shell-env
--------------------------------------------------------------------------------
/.direnv/flake-profile-2-link:
--------------------------------------------------------------------------------
1 | /nix/store/q19smb3y056p32ziwy4kx7rxmma0m1v0-nix-shell-env
--------------------------------------------------------------------------------
/.direnv/flake-profile-3-link:
--------------------------------------------------------------------------------
1 | /nix/store/3z0dhrbjl6ixk8rnflizdxjsqp363dg4-nix-shell-env
--------------------------------------------------------------------------------
/.direnv/flake-profile-4-link:
--------------------------------------------------------------------------------
1 | /nix/store/9virk8id889lkdr93d5sv03wywybxcrq-nix-shell-env
--------------------------------------------------------------------------------
/.direnv/flake-profile-5-link:
--------------------------------------------------------------------------------
1 | /nix/store/lzbfvrsqj2q22h2b99dcicbhcxcy9bqy-nix-shell-env
--------------------------------------------------------------------------------
/.direnv/flake-profile-6-link:
--------------------------------------------------------------------------------
1 | /nix/store/m12racr5hxc041wzs28iivg142fb91qf-nix-shell-env
--------------------------------------------------------------------------------
/.direnv/flake-profile-7-link:
--------------------------------------------------------------------------------
1 | /nix/store/6znavhsxp7n9x5a6fmjy3mnzjwbpzsw0-nix-shell-env
--------------------------------------------------------------------------------
/.direnv/flake-profile-8-link:
--------------------------------------------------------------------------------
1 | /nix/store/mzhw4r4fwncli7rz89h5lfa6fnx2dnlx-nix-shell-env
--------------------------------------------------------------------------------
/.direnv/flake-profile-9-link:
--------------------------------------------------------------------------------
1 | /nix/store/smfaqmssbm1y8qi1iwb6s4a70247j3qg-nix-shell-env
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto eol=lf
2 | *.{cmd,[cC][mM][dD]} text eol=crlf
3 | *.{bat,[bB][aA][tT]} text eol=crlf
4 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | # To get started with Dependabot version updates, you'll need to specify which
2 | # package ecosystems to update and where the package manifests are located.
3 | # Please see the documentation for all configuration options:
4 | # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
5 |
6 | version: 2
7 | updates:
8 | # Maintain dependencies for Go
9 | - package-ecosystem: "gomod"
10 | directory: "/"
11 | schedule:
12 | interval: "weekly"
13 |
14 | # Maintain dependencies for build tools
15 | - package-ecosystem: "gomod"
16 | directory: "/tools"
17 | schedule:
18 | interval: "weekly"
19 |
20 | # Maintain dependencies for GitHub Actions
21 | - package-ecosystem: "github-actions"
22 | directory: "/"
23 | schedule:
24 | interval: "weekly"
25 |
--------------------------------------------------------------------------------
/.github/workflows/Test_CanClonePublicRepoWithoutAuthorizeddKeysAllFormats.yml:
--------------------------------------------------------------------------------
1 | name: e2etest-Test_CanClonePublicRepoWithoutAuthorizeddKeysAllFormats
2 |
3 | on:
4 | push:
5 | branches: [main]
6 | workflow_dispatch:
7 |
8 |
9 | env:
10 | BREV_SETUP_TEST_CMD_DIR: /home/brev/workspace/actions-runner/_work/brev-cli/brev-cli
11 |
12 | jobs:
13 | Test_CanClonePublicRepoWithoutAuthorizeddKeysAllFormats:
14 |
15 | runs-on: [self-hosted]
16 | if: "contains(github.event.head_commit.message, 'e2etest')"
17 | steps:
18 | - uses: actions/checkout@v2
19 | - uses: actions/setup-go@v5
20 | with:
21 | go-version: '1.22.6'
22 | cache: true
23 | - name: expire test cache
24 | run: go clean -testcache
25 | - name: test
26 | run: go test -timeout 240s -run ^Test_CanClonePublicRepoWithoutAuthorizeddKeysAllFormats$ github.com/brevdev/brev-cli/e2etest/setup
27 |
28 | # - name: Report Status
29 | # if: always()
30 | # uses: ravsamhq/notify-slack-action@v1
31 | # with:
32 | # status: ${{ job.status }}
33 | # notify_when: 'failure'
34 | # env:
35 | # SLACK_WEBHOOK_URL: ${{ secrets.ACTION_MONITORING_SLACK }}
36 |
--------------------------------------------------------------------------------
/.github/workflows/Test_ChangePwd.yml:
--------------------------------------------------------------------------------
1 | name: e2etest-Test_ChangePwd
2 |
3 | on:
4 | push:
5 | branches: [main]
6 | workflow_dispatch:
7 |
8 |
9 | env:
10 | BREV_SETUP_TEST_CMD_DIR: /home/brev/workspace/actions-runner/_work/brev-cli/brev-cli
11 |
12 | jobs:
13 | Test_ChangePwd:
14 |
15 | runs-on: [self-hosted]
16 | if: "contains(github.event.head_commit.message, 'e2etest')"
17 | steps:
18 | - uses: actions/checkout@v2
19 | - uses: actions/setup-go@v5
20 | with:
21 | go-version: '1.22.6'
22 | cache: true
23 | - name: expire test cache
24 | run: go clean -testcache
25 | - name: test
26 | run: go test -timeout 240s -run ^Test_ChangePwd$ github.com/brevdev/brev-cli/e2etest/setup
27 |
28 | # - name: Report Status
29 | # if: always()
30 | # uses: ravsamhq/notify-slack-action@v1
31 | # with:
32 | # status: ${{ job.status }}
33 | # notify_when: 'failure'
34 | # env:
35 | # SLACK_WEBHOOK_URL: ${{ secrets.ACTION_MONITORING_SLACK }}
36 |
--------------------------------------------------------------------------------
/.github/workflows/Test_CustomBranchCustomSetupCustomFolder.yml:
--------------------------------------------------------------------------------
1 | name: e2etest-Test_CustomBranchCustomSetupCustomFolder
2 |
3 | on:
4 | push:
5 | branches: [main]
6 | workflow_dispatch:
7 |
8 |
9 | env:
10 | BREV_SETUP_TEST_CMD_DIR: /home/brev/workspace/actions-runner/_work/brev-cli/brev-cli
11 |
12 | jobs:
13 | Test_CustomBranchCustomSetupCustomFolder:
14 |
15 | runs-on: [self-hosted]
16 | if: "contains(github.event.head_commit.message, 'e2etest')"
17 | steps:
18 | - uses: actions/checkout@v2
19 | - uses: actions/setup-go@v5
20 | with:
21 | go-version: '1.22.6'
22 | cache: true
23 | - name: expire test cache
24 | run: go clean -testcache
25 | - name: test
26 | run: go test -timeout 240s -run ^Test_CustomBranchCustomSetupCustomFolder$ github.com/brevdev/brev-cli/e2etest/setup
27 |
28 | # - name: Report Status
29 | # if: always()
30 | # uses: ravsamhq/notify-slack-action@v1
31 | # with:
32 | # status: ${{ job.status }}
33 | # notify_when: 'failure'
34 | # env:
35 | # SLACK_WEBHOOK_URL: ${{ secrets.ACTION_MONITORING_SLACK }}
36 |
--------------------------------------------------------------------------------
/.github/workflows/Test_NoProjectBrev.yml:
--------------------------------------------------------------------------------
1 | name: e2etest-Test_NoProjectBrev
2 |
3 | on:
4 | push:
5 | branches: [main]
6 | workflow_dispatch:
7 |
8 |
9 | env:
10 | BREV_SETUP_TEST_CMD_DIR: /home/brev/workspace/actions-runner/_work/brev-cli/brev-cli
11 |
12 | jobs:
13 | Test_NoProjectBrev:
14 |
15 | runs-on: [self-hosted]
16 | if: "contains(github.event.head_commit.message, 'e2etest')"
17 | steps:
18 | - uses: actions/checkout@v2
19 | - uses: actions/setup-go@v5
20 | with:
21 | go-version: '1.22.6'
22 | cache: true
23 | - name: expire test cache
24 | run: go clean -testcache
25 | - name: test
26 | run: go test -timeout 240s -run ^Test_NoProjectBrev$ github.com/brevdev/brev-cli/e2etest/setup
27 |
28 | # - name: Report Status
29 | # if: always()
30 | # uses: ravsamhq/notify-slack-action@v1
31 | # with:
32 | # status: ${{ job.status }}
33 | # notify_when: 'failure'
34 | # env:
35 | # SLACK_WEBHOOK_URL: ${{ secrets.ACTION_MONITORING_SLACK }}
36 |
--------------------------------------------------------------------------------
/.github/workflows/Test_NoUserBrevNoProj.yml:
--------------------------------------------------------------------------------
1 | name: e2etest-Test_NoUserBrevNoProj
2 |
3 | on:
4 | push:
5 | branches: [main]
6 | workflow_dispatch:
7 |
8 |
9 | env:
10 | BREV_SETUP_TEST_CMD_DIR: /home/brev/workspace/actions-runner/_work/brev-cli/brev-cli
11 |
12 | jobs:
13 | Test_NoUserBrevNoProj:
14 |
15 | runs-on: [self-hosted]
16 | if: "contains(github.event.head_commit.message, 'e2etest')"
17 | steps:
18 | - uses: actions/checkout@v2
19 | - uses: actions/setup-go@v5
20 | with:
21 | go-version: '1.22.6'
22 | cache: true
23 | - name: expire test cache
24 | run: go clean -testcache
25 | - name: test
26 | run: go test -timeout 240s -run ^Test_NoUserBrevNoProj$ github.com/brevdev/brev-cli/e2etest/setup
27 |
28 | # - name: Report Status
29 | # if: always()
30 | # uses: ravsamhq/notify-slack-action@v1
31 | # with:
32 | # status: ${{ job.status }}
33 | # notify_when: 'failure'
34 | # env:
35 | # SLACK_WEBHOOK_URL: ${{ secrets.ACTION_MONITORING_SLACK }}
36 |
--------------------------------------------------------------------------------
/.github/workflows/Test_NoUserBrevProj.yml:
--------------------------------------------------------------------------------
1 | name: e2etest-Test_NoUserBrevProj
2 |
3 | on:
4 | push:
5 | branches: [main]
6 | workflow_dispatch:
7 |
8 |
9 | env:
10 | BREV_SETUP_TEST_CMD_DIR: /home/brev/workspace/actions-runner/_work/brev-cli/brev-cli
11 |
12 | jobs:
13 | Test_NoUserBrevProj:
14 |
15 | runs-on: [self-hosted]
16 | if: "contains(github.event.head_commit.message, 'e2etest')"
17 | steps:
18 | - uses: actions/checkout@v2
19 | - uses: actions/setup-go@v5
20 | with:
21 | go-version: '1.22.6'
22 | cache: true
23 | - name: expire test cache
24 | run: go clean -testcache
25 | - name: test
26 | run: go test -timeout 240s -run ^Test_NoUserBrevProj$ github.com/brevdev/brev-cli/e2etest/setup
27 |
28 | # - name: Report Status
29 | # if: always()
30 | # uses: ravsamhq/notify-slack-action@v1
31 | # with:
32 | # status: ${{ job.status }}
33 | # notify_when: 'failure'
34 | # env:
35 | # SLACK_WEBHOOK_URL: ${{ secrets.ACTION_MONITORING_SLACK }}
36 |
--------------------------------------------------------------------------------
/.github/workflows/Test_ProjectRepoNoBrev.yml:
--------------------------------------------------------------------------------
1 | name: e2etest-Test_ProjectRepoNoBrev
2 |
3 | on:
4 | push:
5 | branches: [main]
6 | workflow_dispatch:
7 |
8 |
9 | env:
10 | BREV_SETUP_TEST_CMD_DIR: /home/brev/workspace/actions-runner/_work/brev-cli/brev-cli
11 |
12 | jobs:
13 | Test_ProjectRepoNoBrev:
14 |
15 | runs-on: [self-hosted]
16 | if: "contains(github.event.head_commit.message, 'e2etest')"
17 | steps:
18 | - uses: actions/checkout@v2
19 | - uses: actions/setup-go@v5
20 | with:
21 | go-version: '1.22.6'
22 | cache: true
23 | - name: expire test cache
24 | run: go clean -testcache
25 | - name: test
26 | run: go test -timeout 240s -run ^Test_ProjectRepoNoBrev$ github.com/brevdev/brev-cli/e2etest/setup
27 |
28 | # - name: Report Status
29 | # if: always()
30 | # uses: ravsamhq/notify-slack-action@v1
31 | # with:
32 | # status: ${{ job.status }}
33 | # notify_when: 'failure'
34 | # env:
35 | # SLACK_WEBHOOK_URL: ${{ secrets.ACTION_MONITORING_SLACK }}
36 |
--------------------------------------------------------------------------------
/.github/workflows/Test_ProvidedSetupFileChange.yml:
--------------------------------------------------------------------------------
1 | name: e2etest-Test_ProvidedSetupFileChange
2 |
3 | on:
4 | push:
5 | branches: [main]
6 | workflow_dispatch:
7 |
8 |
9 | env:
10 | BREV_SETUP_TEST_CMD_DIR: /home/brev/workspace/actions-runner/_work/brev-cli/brev-cli
11 |
12 | jobs:
13 | Test_ProvidedSetupFileChange:
14 |
15 | runs-on: [self-hosted]
16 | if: "contains(github.event.head_commit.message, 'e2etest')"
17 | steps:
18 | - uses: actions/checkout@v2
19 | - uses: actions/setup-go@v5
20 | with:
21 | go-version: '1.22.6'
22 | cache: true
23 | - name: expire test cache
24 | run: go clean -testcache
25 | - name: test
26 | run: go test -timeout 240s -run ^Test_ProvidedSetupFileChange$ github.com/brevdev/brev-cli/e2etest/setup
27 |
28 | # - name: Report Status
29 | # if: always()
30 | # uses: ravsamhq/notify-slack-action@v1
31 | # with:
32 | # status: ${{ job.status }}
33 | # notify_when: 'failure'
34 | # env:
35 | # SLACK_WEBHOOK_URL: ${{ secrets.ACTION_MONITORING_SLACK }}
36 |
--------------------------------------------------------------------------------
/.github/workflows/Test_ProvidedSetupRanNoProj.yml:
--------------------------------------------------------------------------------
1 | name: e2etest-Test_ProvidedSetupRanNoProj
2 |
3 | on:
4 | push:
5 | branches: [main]
6 | workflow_dispatch:
7 |
8 |
9 | env:
10 | BREV_SETUP_TEST_CMD_DIR: /home/brev/workspace/actions-runner/_work/brev-cli/brev-cli
11 |
12 | jobs:
13 | Test_ProvidedSetupRanNoProj:
14 |
15 | runs-on: [self-hosted]
16 | if: "contains(github.event.head_commit.message, 'e2etest')"
17 | steps:
18 | - uses: actions/checkout@v2
19 | - uses: actions/setup-go@v5
20 | with:
21 | go-version: '1.22.6'
22 | cache: true
23 | - name: expire test cache
24 | run: go clean -testcache
25 | - name: test
26 | run: go test -timeout 240s -run ^Test_ProvidedSetupRanNoProj$ github.com/brevdev/brev-cli/e2etest/setup
27 |
28 | # - name: Report Status
29 | # if: always()
30 | # uses: ravsamhq/notify-slack-action@v1
31 | # with:
32 | # status: ${{ job.status }}
33 | # notify_when: 'failure'
34 | # env:
35 | # SLACK_WEBHOOK_URL: ${{ secrets.ACTION_MONITORING_SLACK }}
36 |
--------------------------------------------------------------------------------
/.github/workflows/Test_ProvidedSetupUpdated.yml:
--------------------------------------------------------------------------------
1 | name: e2etest-Test_ProvidedSetupUpdated
2 |
3 | on:
4 | push:
5 | branches: [main]
6 | workflow_dispatch:
7 |
8 |
9 | env:
10 | BREV_SETUP_TEST_CMD_DIR: /home/brev/workspace/actions-runner/_work/brev-cli/brev-cli
11 |
12 | jobs:
13 | Test_ProvidedSetupUpdated:
14 |
15 | runs-on: [self-hosted]
16 | if: "contains(github.event.head_commit.message, 'e2etest')"
17 | steps:
18 | - uses: actions/checkout@v2
19 | - uses: actions/setup-go@v5
20 | with:
21 | go-version: '1.22.6'
22 | cache: true
23 | - name: expire test cache
24 | run: go clean -testcache
25 | - name: test
26 | run: go test -timeout 240s -run ^Test_ProvidedSetupUpdated$ github.com/brevdev/brev-cli/e2etest/setup
27 |
28 | # - name: Report Status
29 | # if: always()
30 | # uses: ravsamhq/notify-slack-action@v1
31 | # with:
32 | # status: ${{ job.status }}
33 | # notify_when: 'failure'
34 | # env:
35 | # SLACK_WEBHOOK_URL: ${{ secrets.ACTION_MONITORING_SLACK }}
36 |
--------------------------------------------------------------------------------
/.github/workflows/Test_UnauthenticatedSSHKey.yml:
--------------------------------------------------------------------------------
1 | name: e2etest-Test_UnauthenticatedSSHKey
2 |
3 | on:
4 | push:
5 | branches: [main]
6 | workflow_dispatch:
7 |
8 |
9 | env:
10 | BREV_SETUP_TEST_CMD_DIR: /home/brev/workspace/actions-runner/_work/brev-cli/brev-cli
11 |
12 | jobs:
13 | Test_UnauthenticatedSSHKey:
14 |
15 | runs-on: [self-hosted]
16 | if: "contains(github.event.head_commit.message, 'e2etest')"
17 | steps:
18 | - uses: actions/checkout@v2
19 | - uses: actions/setup-go@v5
20 | with:
21 | go-version: '1.22.6'
22 | cache: true
23 | - name: expire test cache
24 | run: go clean -testcache
25 | - name: test
26 | run: go test -timeout 240s -run ^Test_UnauthenticatedSSHKey$ github.com/brevdev/brev-cli/e2etest/setup
27 |
28 | - name: Report Status
29 | if: always()
30 | uses: ravsamhq/notify-slack-action@v1
31 | with:
32 | status: ${{ job.status }}
33 | notify_when: 'failure'
34 | env:
35 | SLACK_WEBHOOK_URL: ${{ secrets.ACTION_MONITORING_SLACK }}
36 |
--------------------------------------------------------------------------------
/.github/workflows/Test_UserBrevProjectBrevV0.yml:
--------------------------------------------------------------------------------
1 | name: e2etest-Test_UserBrevProjectBrevV0
2 |
3 | on:
4 | push:
5 | branches: [main]
6 | workflow_dispatch:
7 |
8 |
9 | env:
10 | BREV_SETUP_TEST_CMD_DIR: /home/brev/workspace/actions-runner/_work/brev-cli/brev-cli
11 |
12 | jobs:
13 | Test_UserBrevProjectBrevV0:
14 |
15 | runs-on: [self-hosted]
16 | if: "contains(github.event.head_commit.message, 'e2etest')"
17 | steps:
18 | - uses: actions/checkout@v2
19 | - uses: actions/setup-go@v5
20 | with:
21 | go-version: '1.22.6'
22 | cache: true
23 | - name: expire test cache
24 | run: go clean -testcache
25 | - name: test
26 | run: go test -timeout 240s -run ^Test_UserBrevProjectBrevV0$ github.com/brevdev/brev-cli/e2etest/setup
27 |
28 | # - name: Report Status
29 | # if: always()
30 | # uses: ravsamhq/notify-slack-action@v1
31 | # with:
32 | # status: ${{ job.status }}
33 | # notify_when: 'failure'
34 | # env:
35 | # SLACK_WEBHOOK_URL: ${{ secrets.ACTION_MONITORING_SLACK }}
36 |
--------------------------------------------------------------------------------
/.github/workflows/Test_UserBrevProjectBrevV1All.yml:
--------------------------------------------------------------------------------
1 | name: e2etest-Test_UserBrevProjectBrevV1All
2 |
3 | on:
4 | push:
5 | branches: [main]
6 | workflow_dispatch:
7 |
8 |
9 | env:
10 | BREV_SETUP_TEST_CMD_DIR: /home/brev/workspace/actions-runner/_work/brev-cli/brev-cli
11 |
12 | jobs:
13 | Test_UserBrevProjectBrevV1All:
14 |
15 | runs-on: [self-hosted]
16 | if: "contains(github.event.head_commit.message, 'e2etest')"
17 | steps:
18 | - uses: actions/checkout@v2
19 | - uses: actions/setup-go@v5
20 | with:
21 | go-version: '1.22.6'
22 | cache: true
23 | - name: expire test cache
24 | run: go clean -testcache
25 | - name: test
26 | run: go test -timeout 240s -run ^Test_UserBrevProjectBrevV1All$ github.com/brevdev/brev-cli/e2etest/setup
27 |
28 | # - name: Report Status
29 | # if: always()
30 | # uses: ravsamhq/notify-slack-action@v1
31 | # with:
32 | # status: ${{ job.status }}
33 | # notify_when: 'failure'
34 | # env:
35 | # SLACK_WEBHOOK_URL: ${{ secrets.ACTION_MONITORING_SLACK }}
36 |
--------------------------------------------------------------------------------
/.github/workflows/Test_UserBrevProjectBrevV1Minimal.yml:
--------------------------------------------------------------------------------
1 | name: e2etest-Test_UserBrevProjectBrevV1Minimal
2 |
3 | on:
4 | push:
5 | branches: [main]
6 | workflow_dispatch:
7 |
8 |
9 | env:
10 | BREV_SETUP_TEST_CMD_DIR: /home/brev/workspace/actions-runner/_work/brev-cli/brev-cli
11 |
12 | jobs:
13 | Test_UserBrevProjectBrevV1Minimal:
14 |
15 | runs-on: [self-hosted]
16 | if: "contains(github.event.head_commit.message, 'e2etest')"
17 | steps:
18 | - uses: actions/checkout@v2
19 | - uses: actions/setup-go@v5
20 | with:
21 | go-version: '1.22.6'
22 | cache: true
23 | - name: expire test cache
24 | run: go clean -testcache
25 | - name: test
26 | run: go test -timeout 240s -run ^Test_UserBrevProjectBrevV1Minimal$ github.com/brevdev/brev-cli/e2etest/setup
27 |
28 | # - name: Report Status
29 | # if: always()
30 | # uses: ravsamhq/notify-slack-action@v1
31 | # with:
32 | # status: ${{ job.status }}
33 | # notify_when: 'failure'
34 | # env:
35 | # SLACK_WEBHOOK_URL: ${{ secrets.ACTION_MONITORING_SLACK }}
36 |
--------------------------------------------------------------------------------
/.github/workflows/Test_VscodeExtension.yml:
--------------------------------------------------------------------------------
1 | name: e2etest-Test_VscodeExtension
2 |
3 | on:
4 | push:
5 | branches: [main]
6 | workflow_dispatch:
7 |
8 |
9 | env:
10 | BREV_SETUP_TEST_CMD_DIR: /home/brev/workspace/actions-runner/_work/brev-cli/brev-cli
11 |
12 | jobs:
13 | Test_VscodeExtension:
14 |
15 | runs-on: [self-hosted]
16 | if: "contains(github.event.head_commit.message, 'e2etest')"
17 | steps:
18 | - uses: actions/checkout@v2
19 | - uses: actions/setup-go@v5
20 | with:
21 | go-version: '1.22.6'
22 | cache: true
23 | - name: expire test cache
24 | run: go clean -testcache
25 | - name: test
26 | run: go test -timeout 240s -run ^Test_VscodeExtension$ github.com/brevdev/brev-cli/e2etest/setup
27 |
28 | # - name: Report Status
29 | # if: always()
30 | # uses: ravsamhq/notify-slack-action@v1
31 | # with:
32 | # status: ${{ job.status }}
33 | # notify_when: 'failure'
34 | # env:
35 | # SLACK_WEBHOOK_URL: ${{ secrets.ACTION_MONITORING_SLACK }}
36 |
--------------------------------------------------------------------------------
/.github/workflows/Test_httpGit.yml:
--------------------------------------------------------------------------------
1 | name: e2etest-Test_httpGit
2 |
3 | on:
4 | push:
5 | branches: [main]
6 | workflow_dispatch:
7 |
8 |
9 | env:
10 | BREV_SETUP_TEST_CMD_DIR: /home/brev/workspace/actions-runner/_work/brev-cli/brev-cli
11 |
12 | jobs:
13 | Test_httpGit:
14 |
15 | runs-on: [self-hosted]
16 | if: "contains(github.event.head_commit.message, 'e2etest')"
17 | steps:
18 | - uses: actions/checkout@v2
19 | - uses: actions/setup-go@v5
20 | with:
21 | go-version: '1.22.6'
22 | cache: true
23 | - name: expire test cache
24 | run: go clean -testcache
25 | - name: test
26 | run: go test -timeout 240s -run ^Test_httpGit$ github.com/brevdev/brev-cli/e2etest/setup
27 |
28 | # - name: Report Status
29 | # if: always()
30 | # uses: ravsamhq/notify-slack-action@v1
31 | # with:
32 | # status: ${{ job.status }}
33 | # notify_when: 'failure'
34 | # env:
35 | # SLACK_WEBHOOK_URL: ${{ secrets.ACTION_MONITORING_SLACK }}
36 |
--------------------------------------------------------------------------------
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | name: build
2 |
3 | on:
4 | push:
5 | branches: [main]
6 | pull_request:
7 | branches: [main]
8 | workflow_dispatch:
9 |
10 | jobs:
11 | build:
12 | strategy:
13 | fail-fast: false
14 | matrix:
15 | os: [ubuntu-22.04]
16 | runs-on: ${{ matrix.os }}
17 | defaults:
18 | run:
19 | shell: bash
20 | steps:
21 | - uses: actions/checkout@v2
22 |
23 | - uses: actions/setup-go@v5
24 | with:
25 | go-version: '1.22.6'
26 | cache: true
27 |
28 | - name: Build
29 | run: make fast-build
30 | # - name: Report Status
31 | # if: always()
32 | # uses: ravsamhq/notify-slack-action@v1
33 | # with:
34 | # status: ${{ job.status }}
35 | # notify_when: "failure"
36 | # env:
37 | # SLACK_WEBHOOK_URL: ${{ secrets.ACTION_MONITORING_SLACK }}
38 |
--------------------------------------------------------------------------------
/.github/workflows/codeql-analysis.yml:
--------------------------------------------------------------------------------
1 | name: "CodeQL"
2 |
3 | on:
4 | push:
5 | branches: [ main ]
6 | pull_request:
7 | # The branches below must be a subset of the branches above
8 | branches: [ main ]
9 | schedule:
10 | - cron: '11 0 * * 5'
11 |
12 | jobs:
13 | analyze:
14 | name: Analyze
15 | runs-on: ubuntu-latest
16 |
17 | strategy:
18 | fail-fast: false
19 | matrix:
20 | language: [ 'go' ]
21 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
22 | # Learn more:
23 | # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
24 |
25 | steps:
26 | - name: Checkout repository
27 | uses: actions/checkout@v2
28 |
29 | # Initializes the CodeQL tools for scanning.
30 | - name: Initialize CodeQL
31 | uses: github/codeql-action/init@v2
32 | with:
33 | languages: ${{ matrix.language }}
34 | # If you wish to specify custom queries, you can do so here or in a config file.
35 | # By default, queries listed here will override any specified in a config file.
36 | # Prefix the list here with "+" to use these queries and those in the config file.
37 | # queries: ./path/to/local/query, your-org/your-repo/queries@main
38 |
39 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
40 | # If this step fails, then you should remove it and run the build manually (see below)
41 | - name: Autobuild
42 | uses: github/codeql-action/autobuild@v2
43 |
44 | # ℹ️ Command-line programs to run using the OS shell.
45 | # 📚 https://git.io/JvXDl
46 |
47 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
48 | # and modify them (or add more) to build your code if your project
49 | # uses a compiled language
50 |
51 | #- run: |
52 | # make bootstrap
53 | # make release
54 |
55 | - name: Perform CodeQL Analysis
56 | uses: github/codeql-action/analyze@v2
57 |
--------------------------------------------------------------------------------
/.github/workflows/fmt.yml:
--------------------------------------------------------------------------------
1 | name: fmt
2 |
3 | on:
4 | push:
5 | branches: [main]
6 | pull_request:
7 | branches: [main]
8 | workflow_dispatch:
9 |
10 | jobs:
11 | fmt:
12 | strategy:
13 | fail-fast: false
14 | matrix:
15 | os: [ubuntu-22.04]
16 | runs-on: ${{ matrix.os }}
17 | defaults:
18 | run:
19 | shell: bash
20 | steps:
21 | - uses: actions/checkout@v2
22 |
23 | - uses: actions/setup-go@v5
24 | with:
25 | go-version: '1.22.6'
26 | cache: true
27 |
28 |
29 | - name: install
30 | run: make install-tools
31 | - name: check fmt
32 | run: make fmtcheck
33 | # - name: Report Status
34 | # if: always()
35 | # uses: ravsamhq/notify-slack-action@v1
36 | # with:
37 | # status: ${{ job.status }}
38 | # notify_when: 'failure'
39 | # env:
40 | # SLACK_WEBHOOK_URL: ${{ secrets.ACTION_MONITORING_SLACK }}
41 |
--------------------------------------------------------------------------------
/.github/workflows/legacy.yml:
--------------------------------------------------------------------------------
1 | name: legacy
2 |
3 | on:
4 | push:
5 | branches: [main]
6 | pull_request:
7 | branches: [main]
8 | workflow_dispatch:
9 |
10 | jobs:
11 | ci:
12 | strategy:
13 | fail-fast: false
14 | matrix:
15 | os: [ubuntu-22.04]
16 | runs-on: ${{ matrix.os }}
17 | defaults:
18 | run:
19 | shell: bash
20 | steps:
21 | - uses: actions/checkout@v2
22 |
23 | - uses: actions/setup-go@v5
24 | with:
25 | go-version: '1.22.6'
26 | cache: true
27 | - name: Build
28 | run: make ci
29 | - name: Upload coverage
30 | uses: actions/upload-artifact@v4
31 | with:
32 | name: coverage
33 | path: coverage.*
34 |
35 | - name: Upload dist
36 | uses: actions/upload-artifact@v4
37 | with:
38 | name: dist
39 | path: dist
40 |
41 | - name: Upload coverage to Codecov
42 | uses: codecov/codecov-action@v3.1.0
43 | with:
44 | file: ./coverage.out
45 | flags: ${{ runner.os }}
46 |
47 | # - name: Report Status
48 | # if: always()
49 | # uses: ravsamhq/notify-slack-action@v1
50 | # with:
51 | # status: ${{ job.status }}
52 | # # notify_when: 'failure'
53 | # env:
54 | # SLACK_WEBHOOK_URL: ${{ secrets.ACTION_MONITORING_SLACK }}
55 |
56 | release-test:
57 | runs-on: ubuntu-22.04
58 | steps:
59 | - uses: actions/checkout@v2
60 | with:
61 | fetch-depth: 0
62 |
63 | - uses: actions/setup-go@v5
64 | with:
65 | go-version: '1.22.6'
66 | cache: true
67 | - name: Release test
68 | run: make build
69 |
--------------------------------------------------------------------------------
/.github/workflows/lint.yml:
--------------------------------------------------------------------------------
1 | name: lint
2 |
3 | on:
4 | push:
5 | branches: [main]
6 | pull_request:
7 | branches: [main]
8 | workflow_dispatch:
9 |
10 | jobs:
11 | ci:
12 | strategy:
13 | fail-fast: false
14 | matrix:
15 | os: [ubuntu-22.04]
16 | runs-on: ${{ matrix.os }}
17 | defaults:
18 | run:
19 | shell: bash
20 | steps:
21 | - uses: actions/checkout@v2
22 |
23 | - uses: actions/setup-go@v5
24 | with:
25 | go-version: '1.22.6'
26 | cache: true
27 | - name: install
28 | run: make install-tools
29 | - name: lint
30 | run: make lint
31 | # - name: Report Status
32 | # if: always()
33 | # uses: ravsamhq/notify-slack-action@v1
34 | # with:
35 | # status: ${{ job.status }}
36 | # notify_when: "failure"
37 | # env:
38 | # SLACK_WEBHOOK_URL: ${{ secrets.ACTION_MONITORING_SLACK }}
39 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: release
2 |
3 | on:
4 | push:
5 | tags:
6 | - "v*"
7 |
8 | jobs:
9 | goreleaser:
10 | runs-on: ubuntu-22.04
11 | steps:
12 | - uses: actions/checkout@v2
13 | with:
14 | fetch-depth: 0
15 |
16 | - uses: actions/setup-go@v5
17 | with:
18 | go-version: '1.22.6'
19 | cache: true
20 | - name: Release
21 | run: make ci smoke-test release
22 | env:
23 | GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}
24 | # disable until this until figure out tags
25 | # - name: Repository Dispatch
26 | # uses: peter-evans/repository-dispatch@v2
27 | # with:
28 | # token: ${{ secrets.WORKSPACE_IMAGES_REPO_ACCESS_TOKEN }}
29 | # event-type: brev-cli-release
30 | # repository: brevdev/workspace-images
31 |
--------------------------------------------------------------------------------
/.github/workflows/test.yml:
--------------------------------------------------------------------------------
1 | name: test
2 |
3 | on:
4 | push:
5 | branches: [main]
6 | pull_request:
7 | branches: [main]
8 | workflow_dispatch:
9 |
10 | jobs:
11 | ci:
12 | strategy:
13 | fail-fast: false
14 | matrix:
15 | os: [ubuntu-22.04]
16 | runs-on: ${{ matrix.os }}
17 | defaults:
18 | run:
19 | shell: bash
20 | steps:
21 | - uses: actions/checkout@v2
22 |
23 | - uses: actions/setup-go@v5
24 | with:
25 | go-version: '1.22.6'
26 | cache: true
27 | - name: install
28 | run: make install-tools
29 | - name: test
30 | run: make test
31 | - name: Upload coverage
32 | uses: actions/upload-artifact@v4
33 | with:
34 | name: coverage
35 | path: coverage.*
36 |
37 | - name: Upload dist
38 | uses: actions/upload-artifact@v4
39 | with:
40 | name: dist
41 | path: dist
42 | - name: Upload coverage to Codecov
43 | uses: codecov/codecov-action@v3.1.0
44 | with:
45 | file: ./coverage.out
46 | flags: ${{ runner.os }}
47 |
48 | - name: Report Status
49 | if: always()
50 | uses: ravsamhq/notify-slack-action@v1
51 | with:
52 | status: ${{ job.status }}
53 | notify_when: "failure"
54 | env:
55 | SLACK_WEBHOOK_URL: ${{ secrets.ACTION_MONITORING_SLACK }}
56 |
--------------------------------------------------------------------------------
/.github/workflows/vet.yml:
--------------------------------------------------------------------------------
1 | name: vet
2 |
3 | on:
4 | push:
5 | branches: [main]
6 | pull_request:
7 | branches: [main]
8 | workflow_dispatch:
9 |
10 | jobs:
11 | ci:
12 | strategy:
13 | fail-fast: false
14 | matrix:
15 | os: [ubuntu-22.04]
16 | runs-on: ${{ matrix.os }}
17 | defaults:
18 | run:
19 | shell: bash
20 | steps:
21 | - uses: actions/checkout@v2
22 |
23 | - uses: actions/setup-go@v5
24 | with:
25 | go-version: '1.22.6'
26 | cache: true
27 | - name: install
28 | run: make install-tools
29 | - name: vet
30 | run: make vet
31 | # - name: Report Status
32 | # if: always()
33 | # uses: ravsamhq/notify-slack-action@v1
34 | # with:
35 | # status: ${{ job.status }}
36 | # notify_when: "failure"
37 | # env:
38 | # SLACK_WEBHOOK_URL: ${{ secrets.ACTION_MONITORING_SLACK }}
39 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # MacOS DS_Store files
2 | .DS_Store
3 |
4 | __debug_*
5 |
6 | # Binaries for programs and plugins
7 | *.exe
8 | *.exe~
9 | *.dll
10 | *.so
11 | *.dylib
12 |
13 | # Test binary, built with `go test -c`
14 | *.test
15 |
16 | # Output of the go coverage tool, specifically when used with LiteIDE
17 | out
18 | *.out
19 | coverage.html
20 |
21 | # Dependency directories (remove the comment below to include it)
22 | #vendor/
23 |
24 | # Output of GoReleaser
25 | dist/
26 |
27 | # Visual Studio Code files
28 | .vscode/*
29 | !.vscode/settings.json
30 | !.vscode/tasks.json
31 | !.vscode/launch.json
32 | !.vscode/extensions.json
33 | *.code-workspace
34 |
35 | # Local History for Visual Studio Code
36 | .history/
37 |
38 | # GoLand and IntelliJ IDEA files
39 | .idea/
40 |
41 | # env files that usually contain secrets or local config
42 | # .env
43 | .env
44 | .envrc
45 |
46 | # binary
47 | brev-cli
48 | brev
49 |
50 | # golang executable
51 | go1.*
52 |
53 | devworkspace/**
54 | test.txt
55 | test2.txt
56 | homebrew-brev
57 | flake-explorations
--------------------------------------------------------------------------------
/.golangci.yml:
--------------------------------------------------------------------------------
1 | linters-settings:
2 | goimports:
3 | local-prefixes: github.com/brevdev/brev-cli
4 | revive:
5 | min-confidence: 0.8
6 | rules:
7 | - name: blank-imports
8 | - name: context-as-argument
9 | - name: context-as-argument
10 | - name: context-keys-type
11 | - name: dot-imports
12 | - name: error-return
13 | - name: error-strings
14 | - name: error-naming
15 | - name: if-return
16 | - name: increment-decrement
17 | - name: var-naming
18 | - name: var-declaration
19 | - name: package-comments
20 | - name: range
21 | - name: receiver-naming
22 | - name: time-naming
23 | - name: unexported-return
24 | - name: errorf
25 | - name: empty-block
26 | - name: superfluous-else
27 | - name: unused-parameter
28 | - name: unreachable-code
29 | - name: redefines-builtin-id
30 | gocyclo:
31 | min-complexity: 16
32 | govet:
33 | check-shadowing: true
34 | misspell:
35 | locale: US
36 | nolintlint:
37 | allow-leading-space: false # require machine-readable nolint directives (with no leading space)
38 | allow-unused: false # report any unused nolint directives
39 | require-explanation: true # require an explanation for nolint directives
40 | require-specific: false # don't require nolint directives to be specific about which linter is being skipped
41 | funlen:
42 | lines: 100
43 | wrapcheck:
44 | ignoreSigs:
45 | - .WrapAndTrace
46 | - .Errorf
47 | - .Wrap
48 | - .New
49 | stylecheck:
50 | checks: ["all", "-ST1020", "-ST1000"]
51 |
52 | run:
53 | build-tags:
54 | - codeanalysis
55 |
56 | linters:
57 | # please, do not use `enable-all`: it's deprecated and will be removed soon.
58 | # inverted configuration with `enable-all` and `disable` is not scalable during updates of golangci-lint
59 | disable-all: true
60 | enable:
61 | - errcheck
62 | - gosimple
63 | - govet
64 | - ineffassign
65 | # - staticcheck
66 | - typecheck
67 | - unused
68 | - bodyclose
69 | # - depguard
70 | - dupl
71 | - exportloopref
72 | - forcetypeassert
73 | - funlen
74 | # - gci
75 | - gocognit
76 | # - goconst
77 | - gocritic
78 | - gocyclo
79 | # - godot
80 | - gofumpt
81 | # - revive
82 | # - gomnd
83 | - goprintffuncname
84 | - gosec
85 | # - ifshort
86 | - misspell
87 | - noctx
88 | - nolintlint
89 | - rowserrcheck
90 | - sqlclosecheck
91 | - stylecheck
92 | - thelper
93 | - tparallel
94 | - unconvert
95 | - unparam
96 | # - whitespace
97 | # - errorlint
98 | # - goerr113
99 | - wrapcheck
100 | issues:
101 | # enable issues excluded by default
102 | exclude-use-default: false
103 |
--------------------------------------------------------------------------------
/.goreleaser.yml:
--------------------------------------------------------------------------------
1 | before:
2 | hooks:
3 | - go mod download
4 | builds:
5 | - env:
6 | - CGO_ENABLED=0
7 | goos:
8 | - darwin
9 | - linux
10 | # - windows
11 | goarch:
12 | - amd64
13 | - arm64
14 | binary: brev
15 | ldflags:
16 | - -X github.com/brevdev/brev-cli/pkg/cmd/version.Version={{.Tag}}
17 |
18 | brews:
19 | - name: brev
20 | folder: Formula
21 | # GitHub/GitLab repository to push the formula to
22 | repository:
23 | owner: brevdev
24 | name: homebrew-brev
25 | # Optionally a token can be provided, if it differs from the token provided to GoReleaser
26 | # token: "{{ .Env.HOMEBREW_TAP_GITHUB_TOKEN }}"
27 | # The project name and current git tag are used in the format string.
28 | commit_msg_template: "Brew formula update for {{ .ProjectName }} version {{ .Tag }}"
29 | homepage: "https://docs.brev.dev"
30 | description: "CLI tool for managing workspaces provided by brev.dev"
31 | install: |
32 | bin.install "brev"
33 | generate_completions_from_executable("#{bin}/brev", "completion")
34 | test: |
35 | system "#{bin}/brev" "--version"
36 |
37 | archives:
38 | - format_overrides:
39 | - goos: windows
40 | format: zip
41 | release:
42 | github:
43 | prerelease: auto
44 | # dockers:
45 | # - image_templates:
46 | # - "docker.pkg.github.com/brevdev/brev-cli/{{ .ProjectName }}:latest"
47 | # - "docker.pkg.github.com/brevdev/brev-cli/{{ .ProjectName }}:{{ .Major }}"
48 | # - "docker.pkg.github.com/brevdev/brev-cli/{{ .ProjectName }}:{{ .Major }}.{{ .Minor }}"
49 | # - "docker.pkg.github.com/brevdev/brev-cli/{{ .ProjectName }}:{{ .Major }}.{{ .Minor }}.{{ .Patch }}"
50 | # build_flag_templates:
51 | # - "--pull"
52 | # - "--label=org.opencontainers.image.created={{.Date}}"
53 | # - "--label=org.opencontainers.image.name={{.ProjectName}}"
54 | # - "--label=org.opencontainers.image.revision={{.FullCommit}}"
55 | # - "--label=org.opencontainers.image.version={{.Version}}"
56 | # - "--label=org.opencontainers.image.source={{.GitURL}}"
57 |
--------------------------------------------------------------------------------
/.projectile:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/brevdev/brev-cli/2fb523a140c1f2b720968511c32b5ff80e4eaee9/.projectile
--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": [
3 | "golang.Go"
4 | ]
5 | }
6 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "go.useLanguageServer": true,
3 | "gopls": {
4 | "formatting.gofumpt": true
5 | },
6 | "[go]": {
7 | "editor.formatOnSave": true,
8 | "editor.codeActionsOnSave": {
9 | "source.organizeImports": "explicit"
10 | }
11 | },
12 | "[go.mod]": {
13 | "editor.formatOnSave": true,
14 | "editor.codeActionsOnSave": {
15 | "source.organizeImports": "explicit"
16 | }
17 | },
18 | "go.lintTool": "golangci-lint",
19 | "go.lintFlags": [
20 | "--fast"
21 | ],
22 | "go.lintOnSave": "workspace",
23 | "go.testTimeout": "240s",
24 | "go.goroot": "/home/ubuntu/.asdf/installs/golang//go",
25 | "[go][go.mod]": {
26 | "editor.codeActionsOnSave": {
27 | "source.organizeImports": "explicit"
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/.vscode/tasks.json:
--------------------------------------------------------------------------------
1 | {
2 | // See https://go.microsoft.com/fwlink/?LinkId=733558
3 | // for the documentation about the tasks.json format
4 | "version": "2.0.0",
5 | "tasks": [
6 | {
7 | "label": "make",
8 | "type": "shell",
9 | "command": "make",
10 | "problemMatcher": [
11 | "$go"
12 | ],
13 | "group": {
14 | "kind": "build",
15 | "isDefault": true
16 | }
17 | }
18 | ]
19 | }
20 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | All notable changes to this project will be documented in this file.
4 |
5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/)
6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7 |
8 | ## [Unreleased](https://github.com/brevdev/brev-cli/compare/v0.0.0...HEAD)
9 |
10 | ### Added
11 |
12 | [WIP] Add tailscale vpn client embedded with Brev.
13 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM scratch
2 | COPY brev /
3 | ENTRYPOINT ["/brev"]
4 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2020-2021 Brev.dev Inc.
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining
4 | a copy of this software and associated documentation files (the
5 | "Software"), to deal in the Software without restriction, including
6 | without limitation the rights to use, copy, modify, merge, publish,
7 | distribute, sublicense, and/or sell copies of the Software, and to
8 | permit persons to whom the Software is furnished to do so, subject to
9 | the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be
12 | included in all copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | # Brev.dev
6 |
7 | [](https://console.brev.dev/environment/new?repo=https://github.com/brevdev/brev-cli&instance=2x8)
8 |
9 | [Brev.dev](https://brev.dev) makes it easy to develop on remote machines. Use Brev.dev to start a project and share your intance.
10 |
11 | ## Install the cli
12 |
13 | ### MacOS and Linux
14 |
15 | ```
16 | brew install brevdev/homebrew-brev/brev
17 | ```
18 |
19 | ## Get Started
20 |
21 | https://console.brev.dev
22 |
23 | ## Docs
24 |
25 | https://docs.brev.dev
26 |
27 | ---
28 |
29 | https://user-images.githubusercontent.com/14320477/170176621-6b871798-baef-4d42-affe-063a76eca9da.mp4
30 |
31 | ## Contributing
32 |
33 | We welcome PRs! Checkout [Contributing.md](docs/CONTRIBUTING.md) for more.
34 |
35 | ## Trying other badge styles
36 |
37 | [](https://console.brev.dev/environment/new?repo=https://github.com/brevdev/brev-cli&instance=2x8)
38 |
39 | [](https://console.brev.dev/environment/new?repo=https://github.com/brevdev/brev-cli&instance=2x8)
40 |
--------------------------------------------------------------------------------
/assets/test_noauth_keypair.json:
--------------------------------------------------------------------------------
1 | {
2 | "publicKeyData": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHmlscseepmgVOl5KqhDUHhRVdICXQm8Toh0xq6+NCyL your_email@example.com",
3 | "privateKeyData": "-----BEGIN RSA PRIVATE KEY-----\r\nMIIJKQIBAAKCAgEA8fWyUjqEyTEqtPIXHOvbIz3833Vzp\/PXtszzv8ZJX30M\/29U\r\nGKUAJzEl1yN3VE9\/9sWxoySXzGSm+SVV7jr9KlEKdPHXzxInYgvX8GxjJVzuqImo\r\nfecKmDi2ngU48IK7GyDeITSUgPI6JbHjPC8Sc2+6wVqNiLIzBWAGdIEZ3Txe71GS\r\nwxfMM4jxWTLI4OAH0IpdX5YcDB8oC8SN5ebRe7FhfUI+j0\/H4JFm9x2xLJS9cZuN\r\n7ZR8uRe6n2LqvvokjxPjiJ\/BxjqHNl8hW03gAWcvK2XpL+bsi\/nAu6sZgN92\/Opf\r\nsg2XnL22k3xzx+n8wwxfOOvKEgOIpxHKYfmpCIMNt24\/TtV4f3Vbdqn94AQNd6V9\r\njeKpAFvhjYs8ONNc4VNHpSkBtGCWlxQEsCpDSa9DAUIMdblf3PWKS8KdqOxChpPc\r\nuug0H47Yz5NDLECsG7073awSyvsak3Eo4HPhYza8HYLBlQfHq\/kY3HMFk9m1pvyd\r\nxF6WAB42M65sMlM5RDHlostUp7v\/6+QS4MdrGwqttMhdI6gV1jSVOlQpfsHxWx1V\r\nkYmbOD6LnPziWPUugXaitX0rn9HKOqQA8md0Z75zpBmJsEwcVe0xfMY0XPxRPWqy\r\n2jimDIRIdo5zvB4vTM0GdjHV\/WAaKCvY7ALmXkFPF3OjXFyOariOaIhjZfMCAwEA\r\nAQKCAgBJ1O1LBixKsEQV3uGKo7XEtT+aeF6IW3Hxh+zBEiKFjsUOlMwWkRLQ4sBn\r\nO51IDtI\/XOftnlbrubLxx6DHBx0FcqE8OifeOe3mjzKfXJOMbSUuLINLl9q1xGiF\r\nI5bSXTH2\/zqI62B9UGzJ39Q1IzAAJZHZOmaB1c6Xz0to9ZQM3EUjxcKA4ZwgBaOP\r\n0l1VsUbxLad8aXO5hzBTFvEtvBckJWZYMISienfFYNkIgvjzX5fHOO5AFtVQLZt2\r\n01pKWE4bkrgVkpowgN+Nic3F7Kd0BiZwmbJkOZToyZc0LOulVYsbdfEphdhDregu\r\nbQVvdUj5w6ay2dBZWtayFE2rQ+F97YvTl+Q1AJ8OabydZPEvgIS54P5vDpjg6wWo\r\nnUqn7S2dNVmsuynqShFDkO7\/gMI2dJTC8\/nSHO7i9BWuoPTeeWqnih5gpnoLHgZ+\r\nZDUqbrrE8TZEiIUnfZk+s+8iyWt5KRg9Wp8psj1bXfxxZBbTSVn4K\/iB4MZA1OiC\r\nC\/LcMp0Vi6SKbob6jhJqGoGHrOzs3S+5NP7eLPc9dfGzWbopvbwXhp1p0rdmaHGc\r\nhxYTpDGDWbbKNKqXpMZM1Lf6TPzerIvvzn+pLXN0XAMI7UmANdmuYsQIOA93c3lz\r\nHUcj05AumHtIkKdleUbUKGLYHdAgk9FLqkpGic56LOvYx6\/K0QKCAQEA+I8LvUfI\r\nelpwGbvOKltnsyirmHNwbDX9ujoHguPNHiW0hTn3OJ\/VKxJPsGU+yRLYMpa0vCEU\r\nzS0g4V+CzDR2RDd7kn30bPZRQe8KfedO8hnmMM4AQaOqin\/rl+aBV5WlLxOKZS+X\r\nLjIbcjnVymK8pbjHlhqcvHLl2G6mfh1yXny39KroogG61HRVFrEXNQGmqYX1yg9f\r\nvVLla4TelB6Dv6uHuBGag03JCZaomEMKJxG+wzNE4wVBKJYl6ZuJjNn9kTxAIIUo\r\nD4gvhGLb\/xveVuiy7WoUkM4ROj7ShGviBsojRplDYr1Ji5jao6b\/BN0rf5WlrRy\/\r\nus07xKizJQuwdwKCAQEA+TQTaS4L\/Z26Cj7dtNRP\/ulS2hytIY1PcL8BednTeigD\r\nazdcAARnajZmi4EfteloAP9B1mDeHAlcxgurNEXswf1b5kilFbFa\/FI\/okfv\/4VI\r\nuaC3AHgWZN5la6gh6c3l9m+Gw5ik6g8NweM1CzQD4JWL\/PG79Urk4FnBaX38E9pS\r\nUR8Z4C9Zf0DuBLa7\/yn2hBHTQurspyCDMPhGgeLXrbw4siNQ6d4z1iZ7sZNrPwE+\r\nXwWZkr2TE9a9NJzGXiJX07pEKzSeMHx4WCxY1JUvhFTMYnkY3N7f\/B4W9bLwj7oi\r\nlAx1ZuuQQnBIzjsVxM+jgtib\/9nIsVvzfFVDQgAxZQKCAQEAnyagoqrS4B0GSEPr\r\nZ02toZa6ANxxsKgFdXdwlcuc69\/CrceG13fn+zM3WUAKqp7pVcMPqKIZ+qIZupT4\r\nYB57V4SbGBqUJiy1rN0NP76a2wPgU4GjwmO0cAgmZtXOHbGQ2grOA6osSAUHc+U6\r\nUeNU3VvqV99kWnnLWADJlFjwgTWkaAIDALDQ2vY+AVCVBnivKT7AOYgMimIIygaC\r\nqh67xz9ioGaNI+PrhLs16oCKgKepGL28LwyPQxiY3\/KaaVivNo54lRoNo5xUqJTQ\r\nPpGulMFcyA2za2C2wS+2hdm6GRTW7351GkUPUVYnMMBd69Rd5MyCD80nqsl8qphG\r\nVMMeUwKCAQB6\/NB3oFoamLUwSUZx8DZqwAw7yNtJK8yBAENiN7a\/GvBVAcVN3N6M\r\n9Lw3LUrRJJhHpbKAct4rSBOZSjj8W2Y1dyzbwg53XkhhLtZo6Mfxe34g3shyWtHy\r\nhi\/XqerS0OMldHU2IyeAvF01y0RqewlO1X95HnR84rGCZ8mknqDBy4XEs2y5z6SD\r\nwS+289hkXfljxMhWxkp1UP5uNJnXkHSRMctpXzSXtyouDmANi4vqVFrL2p+oZBcq\r\nO1i1lonv+1MNE2iBSj6n\/0YFfh15DQeeb5tPHiS\/HN++NbtvFxjSVjKqjluCp89S\r\nesfzwAVGVJOGCBE1e+4oWhEY05uV\/zJhAoIBAQDcCH9tLfjSrbdJwsGRRu5eeGy+\r\ndI4oBrD+YmQ48EH01JHunQT9W9XdR6xqomTRf02Ga3oanQQoZgpGXc0yijIaMhIQ\r\nu+Edi9VJCm0rj\/FK6swypksySKhLMQ7DtUvfaP0OlyrJNsCuEEB+kOmkxFkCdjOL\r\ndfRbsZWJh9OcXTQjIvt7Yqs2YHKCG8bqJGlAxp0kOredB3Y2jy7sdgEQKGA3fCQ5\r\nT+aiiljGAP0gBs0Q0r2HToLZowakYd3cjOo\/LCT+0pgdqmxsrBEQKkmVbbERWGIh\r\nOMjWggsIs8S+IyKC8DRFC9JAA45LAmhNedCOnvoQx+wBJajyMZIadr01VYd+\r\n-----END RSA PRIVATE KEY-----\r\n"
4 | }
--------------------------------------------------------------------------------
/assets/test_setup.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | set -euo pipefail
4 |
5 | echo setup!
6 |
--------------------------------------------------------------------------------
/assets/test_workspace.json:
--------------------------------------------------------------------------------
1 | {
2 | "workspaceId": "test_workspace_id",
3 | "workspaceGroupId": "test_workspace_group_id"
4 | }
--------------------------------------------------------------------------------
/bin/gen-e2e-actions.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | import os
3 | import sys
4 | from pathlib import Path
5 |
6 |
7 | def generate_file_content(test_name):
8 | return (
9 | f"name: e2etest-{test_name}\n"
10 | + """
11 | on:
12 | push:
13 | branches: [main]
14 | workflow_dispatch:
15 |
16 |
17 | env:
18 | BREV_SETUP_TEST_CMD_DIR: /home/brev/workspace/actions-runner/_work/brev-cli/brev-cli
19 |
20 | jobs:
21 | """
22 | + f"{test_name}:\n"
23 | + """
24 | runs-on: [self-hosted]
25 | if: "contains(github.event.head_commit.message, 'e2etest')"
26 | steps:
27 | - uses: actions/checkout@v2
28 | - uses: actions/setup-go@v2
29 | with:
30 | go-version: '1.22.6'
31 | cache: true
32 | - name: expire test cache
33 | run: go clean -testcache
34 | - name: test
35 | run: """
36 | + f"go test -timeout 240s -run ^{test_name}$ github.com/brevdev/brev-cli/e2etest/setup\n"
37 | + """
38 | """
39 | )
40 |
41 |
42 | def create_file_if_not_exist(path):
43 | if not os.path.exists(path):
44 | with open(path, "w") as f:
45 | f.write("")
46 |
47 |
48 | if __name__ == "__main__":
49 | if len(sys.argv) < 2:
50 | print("Usage: python gen-e2e-actions.py ")
51 | sys.exit(1)
52 |
53 | file_path_prefix = [".github", "workflows"]
54 | Path("/".join(file_path_prefix)).mkdir(parents=True, exist_ok=True)
55 |
56 | test_names = (name for name in sys.argv[1:] if name != "")
57 | for test_name in test_names:
58 | path = Path("/".join(file_path_prefix + [test_name + ".yml"]))
59 | create_file_if_not_exist(path)
60 | with open(path, "w") as f:
61 | f.write(generate_file_content(test_name))
62 | print(f"Generated e2e-{test_name}.yml")
63 |
--------------------------------------------------------------------------------
/bin/install-gateway-linux.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | set -e
4 |
5 | echo "Start installation..."
6 |
7 | wget --show-progress -qO ./gateway.tar.gz "https://data.services.jetbrains.com/products/download?code=GW&platform=linux&type=eap,rc,release,beta"
8 |
9 | GATEWAY_TEMP_DIR=$(mktemp -d)
10 |
11 | tar -C "$GATEWAY_TEMP_DIR" -xf gateway.tar.gz
12 | rm ./gateway.tar.gz
13 |
14 | "$GATEWAY_TEMP_DIR"/*/bin/gateway.sh
15 |
16 | rm -r "$GATEWAY_TEMP_DIR"
17 |
18 | echo "JetBrains Gateway was successfully installed!"
--------------------------------------------------------------------------------
/bin/install-gateway-mac.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | set -e
4 |
5 | echo "Start installation..."
6 |
7 | wget --show-progress -qO ./gateway.dmg "https://data.services.jetbrains.com/products/download?code=GW&platform=mac&type=eap,rc,release,beta"
8 |
9 | hdiutil attach gateway.dmg
10 | cp -R "/Volumes/JetBrains Gateway/JetBrains Gateway.app" /Applications
11 | hdiutil unmount "/Volumes/JetBrains Gateway"
12 | rm ./gateway.dmg
13 |
14 | echo "JetBrains Gateway successfully installed"
15 |
--------------------------------------------------------------------------------
/bin/install-gateway-macm1.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | set -e
4 |
5 | echo "Start installation..."
6 |
7 | wget --show-progress -qO ./gateway.dmg "https://data.services.jetbrains.com/products/download?code=GW&platform=mac&type=eap,rc,release,beta"
8 |
9 | hdiutil attach gateway.dmg
10 | cp -R "/Volumes/JetBrains Gateway/JetBrains Gateway.app" /Applications
11 | hdiutil unmount "/Volumes/JetBrains Gateway"
12 | rm ./gateway.dmg
13 |
14 | echo "JetBrains Gateway successfully installed"
15 |
--------------------------------------------------------------------------------
/bin/install-latest-linux.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | set -eo pipefail
3 |
4 | # Install the latest version of the Linux binary
5 | DOWNLOAD_URL="$(curl -s https://api.github.com/repos/brevdev/brev-cli/releases/latest | grep "browser_download_url.*linux.*amd64" | cut -d '"' -f 4)"
6 |
7 | # Create temporary directory and ensure cleanup
8 | TMP_DIR="$(mktemp -d)"
9 | trap 'rm -rf "${TMP_DIR}"' EXIT
10 |
11 | # Download the latest release
12 | curl -L "${DOWNLOAD_URL}" -o "${TMP_DIR}/$(basename "${DOWNLOAD_URL}")"
13 |
14 | # Find and extract the archive
15 | ARCHIVE_FILE="$(find "${TMP_DIR}" -name "brev*.tar.gz" -type f)"
16 | tar -xzf "${ARCHIVE_FILE}" -C "${TMP_DIR}"
17 |
18 | # Install the binary to system location
19 | sudo mv "${TMP_DIR}/brev" /usr/local/bin/brev
20 | sudo chmod +x /usr/local/bin/brev
21 |
22 |
--------------------------------------------------------------------------------
/bin/install-latest.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | set -eo pipefail
3 |
4 | # Detect OS and architecture
5 | OS="$(uname -s | tr '[:upper:]' '[:lower:]')"
6 | ARCH="$(uname -m)"
7 | case "${ARCH}" in
8 | x86_64) ARCH="amd64" ;;
9 | aarch64) ARCH="arm64" ;;
10 | esac
11 |
12 | # Get the appropriate download URL for this platform
13 | DOWNLOAD_URL="$(curl -s https://api.github.com/repos/brevdev/brev-cli/releases/latest | grep "browser_download_url.*${OS}.*${ARCH}" | cut -d '"' -f 4)"
14 |
15 | # Verify we found a suitable release
16 | if [ -z "${DOWNLOAD_URL}" ]; then
17 | echo "Error: Could not find release for ${OS} ${ARCH}" >&2
18 | exit 1
19 | fi
20 |
21 | # Create temporary directory and ensure cleanup
22 | TMP_DIR="$(mktemp -d)"
23 | trap 'rm -rf "${TMP_DIR}"' EXIT
24 |
25 | # Download and extract the release
26 | curl -sL "${DOWNLOAD_URL}" -o "${TMP_DIR}/brev.tar.gz"
27 | tar -xzf "${TMP_DIR}/brev.tar.gz" -C "${TMP_DIR}"
28 |
29 | # Install the binary to system location
30 | sudo mv "${TMP_DIR}/brev" /usr/local/bin/brev
31 | sudo chmod +x /usr/local/bin/brev
32 |
33 | echo "Successfully installed brev CLI to /usr/local/bin/brev"
34 |
--------------------------------------------------------------------------------
/bin/newcmd-v2.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | cat < pkg/cmd/$1/$1.go
4 | package $1
5 |
6 | import (
7 | "github.com/spf13/cobra"
8 |
9 | breverrors "github.com/brevdev/brev-cli/pkg/errors"
10 | "github.com/brevdev/brev-cli/pkg/terminal"
11 | )
12 |
13 | var (
14 | short = "TODO"
15 | long = "TODO"
16 | example = "TODO"
17 | )
18 |
19 | func NewCmd$1(t *terminal.Terminal, store $1Store) *cobra.Command {
20 | cmd := &cobra.Command{
21 | Use: "$1",
22 | DisableFlagsInUseLine: true,
23 | Short: short,
24 | Long: long,
25 | Example: example,
26 | RunE: $1{
27 | t: t,
28 | store: store,
29 | }.RunE,
30 | }
31 | return cmd
32 | }
33 |
34 | type $1Store interface {}
35 |
36 | type $1 struct {
37 | t *terminal.Terminal
38 | store $1Store
39 | }
40 |
41 | func ($1 $1) RunE(cmd *cobra.Command, args []string) error {
42 | return breverrors.New("TODO")
43 | }
44 | EOF
45 |
--------------------------------------------------------------------------------
/bin/newcmd.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | cat < pkg/cmd/$1/$1.go
4 | package $1
5 |
6 | import (
7 | "github.com/spf13/cobra"
8 |
9 | breverrors "github.com/brevdev/brev-cli/pkg/errors"
10 | "github.com/brevdev/brev-cli/pkg/terminal"
11 | )
12 |
13 | var (
14 | short = "TODO"
15 | long = "TODO"
16 | example = "TODO"
17 | )
18 |
19 | type $1Store interface {}
20 |
21 | func NewCmd$1(t *terminal.Terminal, store $1Store) *cobra.Command {
22 | cmd := &cobra.Command{
23 | Use: "$1",
24 | DisableFlagsInUseLine: true,
25 | Short: short,
26 | Long: long,
27 | Example: example,
28 | RunE: func(cmd *cobra.Command, args []string) error {
29 | err := Run$1(t, args, store)
30 | if err != nil {
31 | return breverrors.WrapAndTrace(err)
32 | }
33 | return nil
34 | },
35 | }
36 | return cmd
37 | }
38 |
39 | func Run$1(t *terminal.Terminal, args []string, store $1Store) error {
40 | return nil
41 | }
42 | EOF
43 |
--------------------------------------------------------------------------------
/bin/remove-queued-jobs.sh:
--------------------------------------------------------------------------------
1 | token=$GH_TOKEN
2 | repo=brevdev/brev-cli
3 | # get ids of all queued github actions runs for the repo
4 | ids=$(curl -s -H "Authorization: token $token" "https://api.github.com/repos/$repo/actions/runs?status=queued&per_page=100" | jq -r '.workflow_runs[].id')
5 | set -- $ids
6 | for i; do curl \
7 | -H "Authorization: token $token" \
8 | -X POST "https://api.github.com/repos/$repo/actions/runs/$i/cancel"; done
9 |
--------------------------------------------------------------------------------
/flake-explorations/flake-with-both-shell-and-flake.nix:
--------------------------------------------------------------------------------
1 | {
2 | description = "golang development environment ";
3 |
4 | inputs = {
5 | flake-utils.url = "github:numtide/flake-utils";
6 | nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
7 |
8 |
9 | };
10 |
11 | outputs = { self, nixpkgs, flake-utils }:
12 | flake-utils.lib.eachDefaultSystem
13 | (system:
14 | let pkgs = nixpkgs.legacyPackages.${system};
15 | pkgs.currentSystem = system;
16 | pkgs = import (builtins.fetchGit {
17 | # Descriptive name to make the store path easier to identify
18 | name = "my-old-revision";
19 | url = "https://github.com/NixOS/nixpkgs/";
20 | ref = "refs/heads/nixpkgs-unstable";
21 | rev = "ff8b619cfecb98bb94ae49ca7ceca937923a75fa";
22 | }) {};
23 | myPkg = pkgs.golangci-lint;
24 | in
25 | {
26 | # devShell = import ./shell.nix { inherit pkgs; };
27 | devShell = pkgs.mkShell { buildInputs = [
28 | pkgs.go_1_18
29 | pkgs.gopls
30 | pkgs.tmux
31 | pkgs.gofumpt
32 | myPkg
33 | pkgs.gosec
34 | pkgs.delve
35 | pkgs.go-tools
36 | pkgs.gotests
37 | pkgs.gomodifytags
38 | ]; currentSystem = system; };
39 | }
40 | );
41 | }
42 |
--------------------------------------------------------------------------------
/flake-explorations/flake.lock:
--------------------------------------------------------------------------------
1 | {
2 | "nodes": {
3 | "flake-utils": {
4 | "locked": {
5 | "lastModified": 1659877975,
6 | "narHash": "sha256-zllb8aq3YO3h8B/U0/J1WBgAL8EX5yWf5pMj3G0NAmc=",
7 | "owner": "numtide",
8 | "repo": "flake-utils",
9 | "rev": "c0e246b9b83f637f4681389ecabcb2681b4f3af0",
10 | "type": "github"
11 | },
12 | "original": {
13 | "owner": "numtide",
14 | "repo": "flake-utils",
15 | "type": "github"
16 | }
17 | },
18 | "nixpkgs": {
19 | "locked": {
20 | "lastModified": 1665349835,
21 | "narHash": "sha256-UK4urM3iN80UXQ7EaOappDzcisYIuEURFRoGQ/yPkug=",
22 | "owner": "nixos",
23 | "repo": "nixpkgs",
24 | "rev": "34c5293a71ffdb2fe054eb5288adc1882c1eb0b1",
25 | "type": "github"
26 | },
27 | "original": {
28 | "owner": "nixos",
29 | "ref": "nixos-unstable",
30 | "repo": "nixpkgs",
31 | "type": "github"
32 | }
33 | },
34 | "root": {
35 | "inputs": {
36 | "flake-utils": "flake-utils",
37 | "nixpkgs": "nixpkgs"
38 | }
39 | }
40 | },
41 | "root": "root",
42 | "version": 7
43 | }
44 |
--------------------------------------------------------------------------------
/flake-explorations/flake.nix:
--------------------------------------------------------------------------------
1 | # {
2 | # inputs = {
3 | # nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
4 | # };
5 | # outputs = { self, nixpkgs }:
6 | # let
7 | # pkgs = nixpkgs.legacyPackages.x86_64-linux;
8 | # in
9 | # {
10 | # # foo = "bar";
11 | # packages.x86_64-linux.hello = pkgs.hello;
12 | # packages.x86_64-linux.hello2 = pkgs.cowsay;
13 | # devShell.x86_64-linux = pkgs.mkShell {
14 | # buildInputs = [ self.packages.x86_64-linux.hello self.packages.x86_64-linux.hello2 ];
15 | # };
16 |
17 | # };
18 | # }
19 |
20 | {
21 | inputs = {
22 | nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
23 | flake-utils.url = "github:numtide/flake-utils";
24 | };
25 | outputs = { self, nixpkgs, flake-utils }:
26 | flake-utils.lib.eachDefaultSystem (system:
27 | let
28 | pkgs = nixpkgs.legacyPackages.${system};
29 | in
30 | {
31 | # foo = "bar";
32 | packages.hello = pkgs.hello;
33 |
34 | devShell = pkgs.mkShell { buildInputs = [ pkgs.hello pkgs.cowsay ]; };
35 | });
36 | }
37 |
--------------------------------------------------------------------------------
/flake-explorations/result:
--------------------------------------------------------------------------------
1 | /nix/store/gl5a41azbpsadfkfmbilh9yk40dh5dl0-hello-2.12.1
--------------------------------------------------------------------------------
/flake.lock:
--------------------------------------------------------------------------------
1 | {
2 | "nodes": {
3 | "flake-utils": {
4 | "locked": {
5 | "lastModified": 1659877975,
6 | "narHash": "sha256-zllb8aq3YO3h8B/U0/J1WBgAL8EX5yWf5pMj3G0NAmc=",
7 | "owner": "numtide",
8 | "repo": "flake-utils",
9 | "rev": "c0e246b9b83f637f4681389ecabcb2681b4f3af0",
10 | "type": "github"
11 | },
12 | "original": {
13 | "owner": "numtide",
14 | "repo": "flake-utils",
15 | "type": "github"
16 | }
17 | },
18 | "nixpkgs": {
19 | "locked": {
20 | "lastModified": 1665349835,
21 | "narHash": "sha256-UK4urM3iN80UXQ7EaOappDzcisYIuEURFRoGQ/yPkug=",
22 | "owner": "nixos",
23 | "repo": "nixpkgs",
24 | "rev": "34c5293a71ffdb2fe054eb5288adc1882c1eb0b1",
25 | "type": "github"
26 | },
27 | "original": {
28 | "owner": "nixos",
29 | "ref": "nixos-unstable",
30 | "repo": "nixpkgs",
31 | "type": "github"
32 | }
33 | },
34 | "root": {
35 | "inputs": {
36 | "flake-utils": "flake-utils",
37 | "nixpkgs": "nixpkgs"
38 | }
39 | }
40 | },
41 | "root": "root",
42 | "version": 7
43 | }
44 |
--------------------------------------------------------------------------------
/flake.nix:
--------------------------------------------------------------------------------
1 | {
2 | description = "golang instance ";
3 |
4 | inputs = {
5 | flake-utils.url = "github:numtide/flake-utils";
6 | nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
7 | };
8 |
9 | outputs = { self, nixpkgs, flake-utils }:
10 | flake-utils.lib.eachDefaultSystem
11 | (system:
12 | let pkgs = nixpkgs.legacyPackages.${system}; in
13 | {
14 | devShell = import ./shell.nix { inherit pkgs system; };
15 | shellHook = ''
16 | echo "hallo"
17 | '';
18 | }
19 | );
20 | }
21 |
--------------------------------------------------------------------------------
/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "os"
5 |
6 | "github.com/brevdev/brev-cli/pkg/cmd"
7 | "github.com/brevdev/brev-cli/pkg/cmd/cmderrors"
8 | "github.com/brevdev/brev-cli/pkg/errors"
9 | )
10 |
11 | func main() {
12 | done := errors.GetDefaultErrorReporter().Setup()
13 | defer done()
14 | command := cmd.NewDefaultBrevCommand()
15 |
16 | if err := command.Execute(); err != nil {
17 | cmderrors.DisplayAndHandleError(err)
18 | done()
19 | os.Exit(1) //nolint:gocritic // manually call done
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/pkg/analytics/analytics.go:
--------------------------------------------------------------------------------
1 | package analytics
2 |
3 | import (
4 | "bytes"
5 | "context"
6 | "encoding/json"
7 | "net/http"
8 |
9 | "github.com/brevdev/brev-cli/pkg/config"
10 | breverrors "github.com/brevdev/brev-cli/pkg/errors"
11 | )
12 |
13 | type EventData struct {
14 | EventName string `json:"eventName"`
15 | UserID string `json:"userId,omitempty"`
16 | Properties map[string]string `json:"properties,omitempty"`
17 | }
18 |
19 | func TrackEvent(data EventData) error {
20 | conf := config.NewConstants()
21 |
22 | url := conf.GetBrevAPIURl() + "/api/brevent"
23 |
24 | jsonData, err := json.Marshal(data)
25 | if err != nil {
26 | return breverrors.WrapAndTrace(err)
27 | }
28 |
29 | req, err := http.NewRequestWithContext(context.TODO(), "POST", url, bytes.NewBuffer(jsonData))
30 | if err != nil {
31 | return breverrors.WrapAndTrace(err)
32 | }
33 |
34 | req.Header.Set("Content-Type", "application/json")
35 |
36 | client := &http.Client{}
37 | resp, err := client.Do(req)
38 | if err != nil {
39 | return breverrors.WrapAndTrace(err)
40 | }
41 |
42 | //nolint:errcheck //this is common practice
43 | defer resp.Body.Close()
44 |
45 | return nil
46 | }
47 |
--------------------------------------------------------------------------------
/pkg/auth/auth0_test.go:
--------------------------------------------------------------------------------
1 | package auth
2 |
--------------------------------------------------------------------------------
/pkg/autostartconf/aptbinary.go:
--------------------------------------------------------------------------------
1 | package autostartconf
2 |
3 | import (
4 | "path/filepath"
5 | "strings"
6 |
7 | breverrors "github.com/brevdev/brev-cli/pkg/errors"
8 | )
9 |
10 | type AptBinaryConfigurer struct {
11 | LinuxSystemdConfigurer
12 | URL string
13 | Name string
14 | aptDependencies []string
15 | }
16 |
17 | func (abc AptBinaryConfigurer) Install() error {
18 | _ = abc.UnInstall() // best effort
19 |
20 | // install apt dependencies
21 | err := ExecCommands([][]string{
22 | {"apt-get", "update"},
23 | append([]string{"apt-get", "install", "-y"}, abc.aptDependencies...),
24 | })
25 | if err != nil {
26 | if strings.Contains(err.Error(), "dpkg --configure -a") {
27 | err = ExecCommands([][]string{{"sudo dpkg --configure -a"}})
28 | if err != nil {
29 | return breverrors.WrapAndTrace(err)
30 | }
31 | } else {
32 | return breverrors.WrapAndTrace(err)
33 | }
34 | }
35 | // download binary
36 | err = abc.Store.DownloadBinary(
37 | abc.URL,
38 | filepath.Join("/usr/local/bin", abc.Name),
39 | )
40 | if err != nil {
41 | return breverrors.WrapAndTrace(err)
42 | }
43 |
44 | err = abc.Store.WriteString(abc.getDestConfigFile(), abc.ValueConfigFile)
45 | if err != nil {
46 | return breverrors.WrapAndTrace(err)
47 | }
48 |
49 | if ShouldSymlink() {
50 | errother := abc.CreateForcedSymlink()
51 | if errother != nil {
52 | return breverrors.WrapAndTrace(errother)
53 | }
54 | } else {
55 | errother := ExecCommands([][]string{
56 | {"systemctl", "enable", abc.ServiceName},
57 | {"systemctl", "start", abc.ServiceName},
58 | {"systemctl", "daemon-reload"},
59 | })
60 | if errother != nil {
61 | return breverrors.WrapAndTrace(errother)
62 | }
63 | }
64 | return nil
65 | }
66 |
--------------------------------------------------------------------------------
/pkg/autostartconf/autostartconf.go:
--------------------------------------------------------------------------------
1 | package autostartconf
2 |
3 | import (
4 | "fmt"
5 | "os/exec"
6 | "runtime"
7 | )
8 |
9 | const (
10 | targetBin = "/usr/local/bin/brev"
11 | osLinux = "linux"
12 | osDarwin = "darwin"
13 | )
14 |
15 | type AutoStartStore interface {
16 | CopyBin(targetBin string) error
17 | WriteString(path, data string) error
18 | GetOSUser() string
19 | UserHomeDir() (string, error)
20 | Remove(target string) error
21 | FileExists(target string) (bool, error)
22 | DownloadBinary(url string, target string) error
23 | }
24 |
25 | type DaemonConfigurer interface {
26 | Install() error
27 | UnInstall() error
28 | }
29 |
30 | func ExecCommands(commands [][]string) error {
31 | for _, command := range commands {
32 | first, rest := firstAndRest(command)
33 | out, err := exec.Command(first, rest...).CombinedOutput() // #nosec G204
34 | if err != nil {
35 | return fmt.Errorf("error running %s %s: %v, %s", first, fmt.Sprint(command), err, out)
36 | }
37 | }
38 | return nil
39 | }
40 |
41 | func firstAndRest(commandstring []string) (string, []string) {
42 | first := commandstring[0]
43 | rest := commandstring[1:]
44 | return first, rest
45 | }
46 |
47 | // func NewDaemonConfiguration(user string, command string, serviceName string, serviceType string) DaemonConfigurer {
48 | // switch runtime.GOOS {
49 | // case osLinux:
50 | // return LinuxSystemdConfigurer{
51 | // }
52 | // case osDarwin:
53 | // }
54 | // }
55 |
56 | func NewSSHConfigurer(store AutoStartStore) DaemonConfigurer {
57 | switch runtime.GOOS {
58 | case osLinux:
59 | return LinuxSystemdConfigurer{
60 | Store: store,
61 | ValueConfigFile: `
62 | [Install]
63 | WantedBy=multi-user.target
64 |
65 | [Unit]
66 | Description=Brev ssh configurer daemon
67 | After=systemd-user-sessions.service
68 |
69 | [Service]
70 | Type=simple
71 | ExecStart=brev tasks run sshcd --user ` + store.GetOSUser() + `
72 | Restart=always
73 | User=` + store.GetOSUser() + `
74 | `,
75 | ServiceName: "brevsshcd.service",
76 | ServiceType: "user",
77 | TargetBin: targetBin,
78 | }
79 | case osDarwin:
80 | return DarwinPlistConfigurer{
81 | Store: store,
82 | ValueConfigFile: `
83 |
84 |
85 |
86 |
87 |
88 | Label
89 | com.brev.sshcd
90 |
91 | ProgramArguments
92 |
93 | /usr/local/bin/brev
94 | tasks
95 | run
96 | sshcd
97 | --user
98 | ` + store.GetOSUser() + `
99 |
100 |
101 | RunAtLoad
102 |
103 |
104 | StandardOutPath
105 | /var/log/brevsshcd.log
106 | StandardErrorPath
107 | /var/log/brevsshcd.log
108 |
109 |
110 |
111 | `,
112 | ServiceName: "com.brev.sshcd",
113 | ServiceType: SingleUser,
114 | }
115 | }
116 | return nil
117 | }
118 |
--------------------------------------------------------------------------------
/pkg/autostartconf/linux.go:
--------------------------------------------------------------------------------
1 | package autostartconf
2 |
3 | import (
4 | "os"
5 | "path"
6 |
7 | breverrors "github.com/brevdev/brev-cli/pkg/errors"
8 | )
9 |
10 | type LinuxSystemdConfigurer struct {
11 | Store AutoStartStore
12 | ValueConfigFile string
13 | ServiceName string
14 | ServiceType string
15 | TargetBin string
16 | }
17 |
18 | const (
19 | systemDConfigDir = "/etc/systemd/system/"
20 | )
21 |
22 | func (lsc LinuxSystemdConfigurer) getDestConfigFile() string {
23 | return path.Join(systemDConfigDir, lsc.ServiceName)
24 | }
25 |
26 | func (lsc LinuxSystemdConfigurer) UnInstall() error {
27 | exists, err := lsc.Store.FileExists(lsc.getDestConfigFile())
28 | if err != nil {
29 | return breverrors.WrapAndTrace(err)
30 | }
31 | if exists {
32 | errother := lsc.Store.Remove(lsc.getDestConfigFile())
33 | if errother != nil {
34 | return breverrors.WrapAndTrace(errother)
35 | }
36 | }
37 | err = ExecCommands([][]string{
38 | {"systemctl", "disable", lsc.ServiceName},
39 | {"systemctl", "stop", lsc.ServiceName},
40 | })
41 | if err != nil {
42 | return breverrors.WrapAndTrace(err)
43 | }
44 | return nil
45 | }
46 |
47 | func (lsc LinuxSystemdConfigurer) Install() error {
48 | _ = lsc.UnInstall() // best effort
49 | err := lsc.Store.CopyBin(lsc.TargetBin)
50 | if err != nil {
51 | return breverrors.WrapAndTrace(err)
52 | }
53 | err = lsc.Store.WriteString(lsc.getDestConfigFile(), lsc.ValueConfigFile)
54 | if err != nil {
55 | return breverrors.WrapAndTrace(err)
56 | }
57 |
58 | if ShouldSymlink() {
59 | errother := lsc.CreateForcedSymlink()
60 | if errother != nil {
61 | return breverrors.WrapAndTrace(errother)
62 | }
63 | } else {
64 | errother := ExecCommands([][]string{
65 | {"systemctl", "enable", lsc.ServiceName},
66 | {"systemctl", "start", lsc.ServiceName},
67 | {"systemctl", "daemon-reload"},
68 | })
69 | if errother != nil {
70 | return breverrors.WrapAndTrace(errother)
71 | }
72 | }
73 | return nil
74 | }
75 |
76 | // CreateForcedSymlink aims to be the equivalent operation as running
77 | // ln -sf /lib/systemd/system/huproxy.service /etc/systemd/system/default.target.wants/huproxy.service
78 | // which overwrite's an existing symbolic link to point to a different file
79 | // which we need to do in the workspace docker image because systemd isn't running
80 | // at build time.
81 | func (lsc LinuxSystemdConfigurer) CreateForcedSymlink() error {
82 | symlinkTarget := path.Join("/etc/systemd/system/default.target.wants/", lsc.ServiceName)
83 | err := os.Symlink(lsc.getDestConfigFile(), symlinkTarget)
84 | if err != nil {
85 | return breverrors.WrapAndTrace(err)
86 | }
87 | return nil
88 | }
89 |
90 | func ShouldSymlink() bool {
91 | if os.Getenv("SHOULD_SYMLINK") != "" {
92 | return os.Getenv("SHOULD_SYMLINK") == "1"
93 | }
94 | return false
95 | }
96 |
--------------------------------------------------------------------------------
/pkg/autostartconf/staticbinary.go:
--------------------------------------------------------------------------------
1 | package autostartconf
2 |
3 | type StaticBinaryConfigurer struct {
4 | LinuxSystemdConfigurer
5 | URL string
6 | Name string
7 | }
8 |
9 | // best effort
10 |
11 | // download binary
12 |
--------------------------------------------------------------------------------
/pkg/cmd/background/background_test.go:
--------------------------------------------------------------------------------
1 | package background
2 |
--------------------------------------------------------------------------------
/pkg/cmd/clipboard/clipboard_listener.go:
--------------------------------------------------------------------------------
1 | package clipboard
2 |
3 | import (
4 | "context"
5 | "fmt"
6 | "io/ioutil"
7 | "net/http"
8 | "strings"
9 |
10 | breverrors "github.com/brevdev/brev-cli/pkg/errors"
11 | "github.com/gin-gonic/gin"
12 | )
13 |
14 | // Server ...
15 | type Server struct {
16 | host string
17 | port string
18 | }
19 |
20 | // Config ...
21 | type Config struct {
22 | Host string
23 | Port string
24 | }
25 |
26 | // New ...
27 | func CreateListener(config *Config) *Server {
28 | return &Server{
29 | host: config.Host,
30 | port: config.Port,
31 | }
32 | }
33 |
34 | // tcp req
35 | func SendRequest(address string, message string) error {
36 | reader := strings.NewReader(message)
37 | request, err := http.NewRequestWithContext(context.TODO(), "GET", "http://"+address+"/", reader)
38 | if err != nil {
39 | fmt.Println(err)
40 | return breverrors.WrapAndTrace(err)
41 | }
42 | client := &http.Client{}
43 | resp, err := client.Do(request)
44 | fmt.Println(resp)
45 | if err != nil {
46 | fmt.Println(err)
47 | return breverrors.WrapAndTrace(err)
48 | }
49 | defer resp.Body.Close() //nolint:errcheck //deving and defer
50 | return nil
51 | }
52 |
53 | // Run ...
54 | func (server *Server) Run() {
55 | // Starts a new Gin instance with no middle-ware
56 | r := gin.New()
57 |
58 | r.GET("/", func(c *gin.Context) {
59 | jsonData, err := ioutil.ReadAll(c.Request.Body)
60 | if err != nil {
61 | // Handle error
62 | c.String(http.StatusBadRequest, "Can't parse body")
63 | }
64 | SaveToClipboard(string(jsonData))
65 | c.String(http.StatusOK, "success")
66 | })
67 | err := r.Run(server.host + ":" + server.port)
68 | if err != nil {
69 | fmt.Println(err)
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/pkg/cmd/cmderrors/cmderrors.go:
--------------------------------------------------------------------------------
1 | package cmderrors
2 |
3 | import (
4 | "fmt"
5 | "os"
6 | "os/exec"
7 | "strings"
8 |
9 | "github.com/getsentry/sentry-go"
10 | "github.com/pkg/errors"
11 | "github.com/spf13/cobra"
12 |
13 | "github.com/brevdev/brev-cli/pkg/featureflag"
14 | "github.com/brevdev/brev-cli/pkg/terminal"
15 |
16 | breverrors "github.com/brevdev/brev-cli/pkg/errors"
17 | )
18 |
19 | // determines if should print error stack trace and/or send to crash monitor
20 |
21 | func DisplayAndHandleError(err error) {
22 | er := breverrors.GetDefaultErrorReporter()
23 | er.AddBreadCrumb(breverrors.ErrReportBreadCrumb{
24 | Type: "default",
25 | Category: "stacktrace",
26 | Level: string(sentry.LevelError),
27 | Message: err.Error(),
28 | })
29 | if err != nil {
30 | t := terminal.New()
31 | prettyErr := ""
32 | switch errors.Cause(err).(type) {
33 | case breverrors.ValidationError:
34 | // do not report error
35 | prettyErr = (t.Yellow(errors.Cause(err).Error()))
36 | case breverrors.WorkspaceNotRunning: // report error to track when this occurs, but don't print stacktrace to user unless in dev mode
37 | er.ReportError(err)
38 | prettyErr = (t.Yellow(errors.Cause(err).Error()))
39 | case *breverrors.NvidiaMigrationError:
40 | // Handle nvidia migration error
41 | if nvErr, ok := errors.Cause(err).(*breverrors.NvidiaMigrationError); ok {
42 | fmt.Println("\n This account has been migrated to NVIDIA Auth. Attempting to log in with NVIDIA account...")
43 | brevBin, err1 := os.Executable()
44 | if err1 == nil {
45 | cmd := exec.Command(brevBin, "login", "--auth", "nvidia") // #nosec G204
46 | cmd.Stdout = os.Stdout
47 | cmd.Stderr = os.Stderr
48 | cmd.Stdin = os.Stdin
49 | loginErr := cmd.Run() // If automatic login succeeds, we'll exit without showing the original error
50 | if loginErr == nil {
51 | // Login successful, don't show the original error
52 | return
53 | }
54 | }
55 | // Only show the error if automatic login failed or couldn't be attempted
56 | prettyErr = t.Yellow(nvErr.Error() + "\n" + nvErr.Directive())
57 | } else {
58 | // Fallback in case type assertion fails (shouldn't happen but better safe than sorry)
59 | prettyErr = t.Red(errors.Cause(err).Error())
60 | er.ReportError(err)
61 | }
62 | default:
63 | if isSneakyValidationErr(err) {
64 | prettyErr = (t.Yellow(errors.Cause(err).Error()))
65 | } else {
66 | er.ReportError(err)
67 | prettyErr = (t.Red(errors.Cause(err).Error()))
68 | }
69 | }
70 | if featureflag.Debug() || featureflag.IsDev() {
71 | fmt.Println(err)
72 | } else {
73 | fmt.Println(prettyErr)
74 | }
75 | }
76 | }
77 |
78 | func isSneakyValidationErr(err error) bool {
79 | return strings.Contains(err.Error(), "unknown flag:") || strings.Contains(err.Error(), "unknown command")
80 | }
81 |
82 | func TransformToValidationError(pa cobra.PositionalArgs) cobra.PositionalArgs {
83 | return func(cmd *cobra.Command, args []string) error {
84 | err := pa(cmd, args)
85 | if err != nil {
86 | return breverrors.NewValidationError(err.Error())
87 | }
88 | return nil
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/pkg/cmd/cmderrors/cmderrors_test.go:
--------------------------------------------------------------------------------
1 | package cmderrors
2 |
--------------------------------------------------------------------------------
/pkg/cmd/completions/completions.go:
--------------------------------------------------------------------------------
1 | package completions
2 |
3 | import (
4 | "github.com/brevdev/brev-cli/pkg/entity"
5 | "github.com/brevdev/brev-cli/pkg/store"
6 | "github.com/brevdev/brev-cli/pkg/terminal"
7 | "github.com/spf13/cobra"
8 | )
9 |
10 | type CompletionStore interface {
11 | GetWorkspaces(organizationID string, options *store.GetWorkspacesOptions) ([]entity.Workspace, error)
12 | GetActiveOrganizationOrDefault() (*entity.Organization, error)
13 | GetCurrentUser() (*entity.User, error)
14 | GetOrganizations(options *store.GetOrganizationsOptions) ([]entity.Organization, error)
15 | }
16 |
17 | type CompletionHandler func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective)
18 |
19 | func GetAllWorkspaceNameCompletionHandler(completionStore CompletionStore, t *terminal.Terminal) CompletionHandler {
20 | return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
21 | user, err := completionStore.GetCurrentUser()
22 | if err != nil {
23 | t.Errprint(err, "")
24 | return nil, cobra.ShellCompDirectiveError
25 | }
26 |
27 | org, err := completionStore.GetActiveOrganizationOrDefault()
28 | if err != nil {
29 | t.Errprint(err, "")
30 | return nil, cobra.ShellCompDirectiveError
31 | }
32 | if org == nil {
33 | return []string{}, cobra.ShellCompDirectiveDefault
34 | }
35 |
36 | workspaces, err := completionStore.GetWorkspaces(org.ID, &store.GetWorkspacesOptions{UserID: user.ID})
37 | if err != nil {
38 | t.Errprint(err, "")
39 | return nil, cobra.ShellCompDirectiveError
40 | }
41 |
42 | workspaceNames := []string{}
43 | for _, w := range workspaces {
44 | workspaceNames = append(workspaceNames, w.Name)
45 | }
46 |
47 | return workspaceNames, cobra.ShellCompDirectiveDefault
48 | }
49 | }
50 |
51 | func GetOrgsNameCompletionHandler(completionStore CompletionStore, t *terminal.Terminal) CompletionHandler {
52 | return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
53 | orgs, err := completionStore.GetOrganizations(nil)
54 | if err != nil {
55 | t.Errprint(err, "")
56 | return nil, cobra.ShellCompDirectiveError
57 | }
58 |
59 | orgNames := []string{}
60 | for _, o := range orgs {
61 | orgNames = append(orgNames, o.Name)
62 | }
63 |
64 | return orgNames, cobra.ShellCompDirectiveDefault
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/pkg/cmd/connect/connect.go:
--------------------------------------------------------------------------------
1 | package connect
2 |
3 | import (
4 | "github.com/spf13/cobra"
5 |
6 | breverrors "github.com/brevdev/brev-cli/pkg/errors"
7 | "github.com/brevdev/brev-cli/pkg/terminal"
8 | )
9 |
10 | var (
11 | short = "Connect Brev to your AWS account"
12 | long = "Connect Brev to your AWS account"
13 | example = "brev connect"
14 | )
15 |
16 | type connectStore interface{}
17 |
18 | func NewCmdConnect(t *terminal.Terminal, store connectStore) *cobra.Command {
19 | cmd := &cobra.Command{
20 | Use: "connect-aws",
21 | DisableFlagsInUseLine: true,
22 | Short: short,
23 | Long: long,
24 | Example: example,
25 | RunE: func(cmd *cobra.Command, args []string) error {
26 | err := RunConnect(t, args, store)
27 | if err != nil {
28 | return breverrors.WrapAndTrace(err)
29 | }
30 | return nil
31 | },
32 | }
33 | return cmd
34 | }
35 |
36 | func RunConnect(t *terminal.Terminal, _ []string, _ connectStore) error {
37 | t.Vprintf("Connect the AWS IAM user to create instances in your AWS account.\n")
38 | t.Vprintf(t.Yellow("\tFollow the guide here: %s", "https://onboarding.brev.dev/connect-aws\n\n"))
39 | // t.Vprintf(t.Yellow("Connect the AWS IAM user to create dev environments in your AWS account.\n\n"))
40 |
41 | AccessKeyID := terminal.PromptGetInput(terminal.PromptContent{
42 | Label: "Access Key ID: ",
43 | ErrorMsg: "error",
44 | })
45 |
46 | SecretAccessKey := terminal.PromptGetInput(terminal.PromptContent{
47 | Label: "Secret Access Key: ",
48 | ErrorMsg: "error",
49 | Mask: '*',
50 | })
51 |
52 | t.Vprintf("\n")
53 | t.Vprintf(AccessKeyID)
54 | t.Vprintf(SecretAccessKey)
55 |
56 | return nil
57 | }
58 |
--------------------------------------------------------------------------------
/pkg/cmd/create/create_test.go:
--------------------------------------------------------------------------------
1 | package create
2 |
--------------------------------------------------------------------------------
/pkg/cmd/delete/delete.go:
--------------------------------------------------------------------------------
1 | package delete
2 |
3 | import (
4 | _ "embed"
5 | "fmt"
6 | "strings"
7 |
8 | "github.com/brevdev/brev-cli/pkg/cmd/completions"
9 | "github.com/brevdev/brev-cli/pkg/cmd/util"
10 | "github.com/brevdev/brev-cli/pkg/entity"
11 | breverrors "github.com/brevdev/brev-cli/pkg/errors"
12 | "github.com/brevdev/brev-cli/pkg/terminal"
13 | "github.com/hashicorp/go-multierror"
14 | "github.com/spf13/cobra"
15 | stripmd "github.com/writeas/go-strip-markdown"
16 | )
17 |
18 | var (
19 | //go:embed doc.md
20 | deleteLong string
21 | deleteExample = "brev delete "
22 | )
23 |
24 | type DeleteStore interface {
25 | completions.CompletionStore
26 | DeleteWorkspace(workspaceID string) (*entity.Workspace, error)
27 | GetWorkspaceByNameOrID(orgID string, nameOrID string) ([]entity.Workspace, error)
28 | }
29 |
30 | func NewCmdDelete(t *terminal.Terminal, loginDeleteStore DeleteStore, noLoginDeleteStore DeleteStore) *cobra.Command {
31 | cmd := &cobra.Command{
32 | Annotations: map[string]string{"workspace": ""},
33 | Use: "delete",
34 | DisableFlagsInUseLine: true,
35 | Short: "Delete a Brev instance",
36 | Long: stripmd.Strip(deleteLong),
37 | Example: deleteExample,
38 | ValidArgsFunction: completions.GetAllWorkspaceNameCompletionHandler(noLoginDeleteStore, t),
39 | RunE: func(cmd *cobra.Command, args []string) error {
40 | var allError error
41 | for _, workspace := range args {
42 | err := deleteWorkspace(workspace, t, loginDeleteStore)
43 | if err != nil {
44 | allError = multierror.Append(allError, err)
45 | }
46 | }
47 | if allError != nil {
48 | return breverrors.WrapAndTrace(allError)
49 | }
50 | return nil
51 | },
52 | }
53 |
54 | return cmd
55 | }
56 |
57 | func deleteWorkspace(workspaceName string, t *terminal.Terminal, deleteStore DeleteStore) error {
58 | workspace, err := util.GetUserWorkspaceByNameOrIDErr(deleteStore, workspaceName)
59 | if err != nil {
60 | err1 := handleAdminUser(err, deleteStore)
61 | if err1 != nil {
62 | return breverrors.WrapAndTrace(err1)
63 | }
64 | }
65 |
66 | var workspaceID string
67 | if workspace != nil {
68 | workspaceID = workspace.ID
69 | } else {
70 | workspaceID = workspaceName
71 | }
72 |
73 | deletedWorkspace, err := deleteStore.DeleteWorkspace(workspaceID)
74 | if err != nil {
75 | return breverrors.WrapAndTrace(err)
76 | }
77 |
78 | t.Vprintf("Deleting instance %s. This can take a few minutes. Run 'brev ls' to check status\n", deletedWorkspace.Name)
79 |
80 | return nil
81 | }
82 |
83 | func handleAdminUser(err error, deleteStore DeleteStore) error {
84 | if strings.Contains(err.Error(), "not found") {
85 | user, err1 := deleteStore.GetCurrentUser()
86 | if err1 != nil {
87 | return breverrors.WrapAndTrace(err1)
88 | }
89 | if user.GlobalUserType != "Admin" {
90 | return breverrors.WrapAndTrace(err)
91 | }
92 | fmt.Println("attempting to delete a workspace you don't own as admin")
93 | return nil
94 | }
95 |
96 | if err != nil {
97 | return breverrors.WrapAndTrace(err)
98 | }
99 |
100 | return nil
101 | }
102 |
--------------------------------------------------------------------------------
/pkg/cmd/delete/delete_test.go:
--------------------------------------------------------------------------------
1 | package delete
2 |
--------------------------------------------------------------------------------
/pkg/cmd/delete/doc.md:
--------------------------------------------------------------------------------
1 | Delete a Workspace by name or ID.
2 |
3 | ## SYNOPSIS
4 |
5 | ```
6 | brev delete [ Workspace Name or ID... ]
7 | ```
8 |
9 | ## DESCRIPTION
10 |
11 | Deleting a workspace will permanently delete a workspace from your account.
12 | This command will delete all content in the workspace and any volumes associated
13 | with the workspace. This command is not reversable and can result in lost work.
14 |
15 | ## EXAMPLE
16 |
17 | ### Delete a workspace
18 |
19 | ```
20 | $ brev delete payments-frontend
21 | Deleting workspace payments-frontend. This can take a few minutes. Run 'brev ls' to check status
22 | ```
23 |
24 | #### Delete multiple workspaces
25 |
26 | ```
27 | $ brev delete bar euler54 naive-pubsub jupyter
28 | Deleting workspace bar. This can take a few minutes. Run 'brev ls' to check status
29 | Deleting workspace euler54. This can take a few minutes. Run 'brev ls' to check status
30 | Deleting workspace naive-pubsub. This can take a few minutes. Run 'brev ls' to check status
31 | Deleting workspace jupyter. This can take a few minutes. Run 'brev ls' to check status
32 |
33 | ```
34 |
35 | ## SEE ALSO
36 |
37 | TODO
38 |
--------------------------------------------------------------------------------
/pkg/cmd/envvars/envvars.go:
--------------------------------------------------------------------------------
1 | package envvars
2 |
3 | import (
4 | breverrors "github.com/brevdev/brev-cli/pkg/errors"
5 | "github.com/brevdev/brev-cli/pkg/terminal"
6 | "github.com/spf13/cobra"
7 | )
8 |
9 | type EnvVarsStore interface{}
10 |
11 | func NewCmdEnvVars(_ *terminal.Terminal, evStore EnvVarsStore) *cobra.Command {
12 | cmd := &cobra.Command{
13 | Annotations: map[string]string{"housekeeping": ""},
14 | Use: "env-vars",
15 | DisableFlagsInUseLine: true,
16 | Short: "configure env vars in supported shells",
17 | Long: "Import your IDE config",
18 | Example: "",
19 | RunE: func(cmd *cobra.Command, args []string) error {
20 | err := RunEnvVars(evStore)
21 | if err != nil {
22 | return breverrors.WrapAndTrace(err)
23 | }
24 | return nil
25 | },
26 | }
27 |
28 | return cmd
29 | }
30 |
31 | func RunEnvVars(_ EnvVarsStore) error {
32 | return nil
33 | }
34 |
--------------------------------------------------------------------------------
/pkg/cmd/envvars/envvars_test.go:
--------------------------------------------------------------------------------
1 | package envvars
2 |
--------------------------------------------------------------------------------
/pkg/cmd/fu/fu.go:
--------------------------------------------------------------------------------
1 | package fu
2 |
3 | import (
4 | "fmt"
5 | "time"
6 |
7 | "github.com/brevdev/brev-cli/pkg/cmd/completions"
8 | "github.com/brevdev/brev-cli/pkg/entity"
9 | breverrors "github.com/brevdev/brev-cli/pkg/errors"
10 | "github.com/brevdev/brev-cli/pkg/store"
11 | "github.com/brevdev/brev-cli/pkg/terminal"
12 | "github.com/hashicorp/go-multierror"
13 | "github.com/spf13/cobra"
14 | stripmd "github.com/writeas/go-strip-markdown"
15 | )
16 |
17 | var (
18 | fuLong string
19 | fuExample = "brev fu "
20 | )
21 |
22 | type FuStore interface {
23 | completions.CompletionStore
24 | DeleteWorkspace(workspaceID string) (*entity.Workspace, error)
25 | GetWorkspaces(organizationID string, options *store.GetWorkspacesOptions) ([]entity.Workspace, error)
26 | BanUser(userID string) error
27 | GetWorkspaceByNameOrID(orgID string, nameOrID string) ([]entity.Workspace, error)
28 | GetAllOrgsAsAdmin(userID string) ([]entity.Organization, error)
29 | }
30 |
31 | func NewCmdFu(t *terminal.Terminal, loginFuStore FuStore, noLoginFuStore FuStore) *cobra.Command {
32 | cmd := &cobra.Command{
33 | Annotations: map[string]string{"workspace": ""},
34 | Use: "fu",
35 | DisableFlagsInUseLine: true,
36 | Short: "Fetch all workspaces for a user and delete them",
37 | Long: stripmd.Strip(fuLong),
38 | Example: fuExample,
39 | ValidArgsFunction: completions.GetAllWorkspaceNameCompletionHandler(noLoginFuStore, t),
40 | RunE: func(cmd *cobra.Command, args []string) error {
41 | var allError error
42 | for _, userID := range args {
43 | err := fuUser(userID, t, loginFuStore)
44 | if err != nil {
45 | allError = multierror.Append(allError, err)
46 | }
47 | }
48 | if allError != nil {
49 | return breverrors.WrapAndTrace(allError)
50 | }
51 | return nil
52 | },
53 | }
54 |
55 | return cmd
56 | }
57 |
58 | func fuUser(userID string, t *terminal.Terminal, fuStore FuStore) error {
59 | orgs, err := fuStore.GetAllOrgsAsAdmin(userID)
60 | if err != nil {
61 | return breverrors.WrapAndTrace(err)
62 | }
63 |
64 | var allWorkspaces []entity.Workspace
65 | for _, org := range orgs {
66 | workspaces, errr := fuStore.GetWorkspaces(org.ID, nil)
67 | if errr != nil {
68 | return breverrors.WrapAndTrace(errr)
69 | }
70 | allWorkspaces = append(allWorkspaces, workspaces...)
71 | }
72 |
73 | s := t.NewSpinner()
74 | s.Suffix = " Fetching workspaces for user " + userID
75 | s.Start()
76 | time.Sleep(5 * time.Second)
77 | s.Stop()
78 |
79 | confirm := terminal.PromptGetInput(terminal.PromptContent{
80 | Label: fmt.Sprintf("Are you sure you want to delete all %d workspaces for user %s? (y/n)", len(allWorkspaces), userID),
81 | ErrorMsg: "You must confirm to proceed.",
82 | AllowEmpty: false,
83 | })
84 | if confirm != "y" {
85 | return nil
86 | }
87 |
88 | for _, workspace := range allWorkspaces {
89 | _, err2 := fuStore.DeleteWorkspace(workspace.ID)
90 | if err2 != nil {
91 | t.Vprintf(t.Red("Failed to delete workspace with ID: %s\n", workspace.ID))
92 | t.Vprintf(t.Red("Error: %s\n", err.Error()))
93 | continue
94 | }
95 | t.Vprintf("✅ Deleted workspace %s\n", workspace.Name)
96 | }
97 |
98 | err = fuStore.BanUser(userID)
99 | if err != nil {
100 | t.Vprintf(t.Red("Failed to ban user with ID: %s\n", userID))
101 | t.Vprintf(t.Red("Error: %s\n", err.Error()))
102 | }
103 | t.Vprint("\n")
104 | t.Vprintf("🖕 Banned user %s\n", userID)
105 |
106 | return nil
107 | }
108 |
--------------------------------------------------------------------------------
/pkg/cmd/fu/fu_test.go:
--------------------------------------------------------------------------------
1 | package fu
2 |
--------------------------------------------------------------------------------
/pkg/cmd/healthcheck/healthcheck.go:
--------------------------------------------------------------------------------
1 | // Package healthcheck checks the health of the backend
2 | package healthcheck
3 |
4 | import (
5 | "github.com/brevdev/brev-cli/pkg/cmdcontext"
6 | breverrors "github.com/brevdev/brev-cli/pkg/errors"
7 | "github.com/brevdev/brev-cli/pkg/terminal"
8 |
9 | "github.com/spf13/cobra"
10 | )
11 |
12 | type HealthcheckStore interface {
13 | Healthcheck() error
14 | }
15 |
16 | func NewCmdHealthcheck(t *terminal.Terminal, store HealthcheckStore) *cobra.Command {
17 | cmd := &cobra.Command{
18 | Annotations: map[string]string{"housekeeping": ""},
19 | Use: "healthcheck",
20 | Short: "Check backend to see if it's healthy",
21 | Long: "Check backend to see if it's healthy",
22 | Example: `brev healthcheck`,
23 | PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
24 | err := cmdcontext.InvokeParentPersistentPreRun(cmd, args)
25 | if err != nil {
26 | return breverrors.WrapAndTrace(err)
27 | }
28 |
29 | return nil
30 | },
31 | RunE: func(cmd *cobra.Command, args []string) error {
32 | err := healthcheck(t, store)
33 | if err != nil {
34 | return breverrors.WrapAndTrace(err)
35 | }
36 | return nil
37 | },
38 | }
39 |
40 | return cmd
41 | }
42 |
43 | func healthcheck(t *terminal.Terminal, store HealthcheckStore) error {
44 | err := store.Healthcheck()
45 | if err != nil {
46 | t.Print("Not Healthy!")
47 | return breverrors.WrapAndTrace(err)
48 | }
49 | t.Print("Healthy!")
50 | return nil
51 | }
52 |
--------------------------------------------------------------------------------
/pkg/cmd/hello/hello_test.go:
--------------------------------------------------------------------------------
1 | package hello
2 |
--------------------------------------------------------------------------------
/pkg/cmd/importideconfig/importideconfig_test.go:
--------------------------------------------------------------------------------
1 | package importideconfig
2 |
--------------------------------------------------------------------------------
/pkg/cmd/initfile/initfile.go:
--------------------------------------------------------------------------------
1 | package initfile
2 |
3 | import (
4 | "github.com/brevdev/brev-cli/pkg/mergeshells"
5 | "github.com/brevdev/brev-cli/pkg/terminal"
6 | "github.com/spf13/cobra"
7 | )
8 |
9 | type InitFileStore interface {
10 | GetFileAsString(path string) (string, error)
11 | }
12 |
13 | func NewCmdInitFile(t *terminal.Terminal, store InitFileStore) *cobra.Command {
14 | cmd := &cobra.Command{
15 | Use: "init",
16 | DisableFlagsInUseLine: true,
17 | Short: "initialize a .brev/setup.sh file if it does not exist",
18 | Long: "initialize a .brev/setup.sh file if it does not exist",
19 | RunE: func(cmd *cobra.Command, args []string) error {
20 | if len(args) > 0 {
21 | // then assume it is .
22 | mergeshells.ImportPath(t, args[0], store)
23 | } else {
24 | mergeshells.ImportPath(t, ".", store)
25 | }
26 | return nil
27 | },
28 | }
29 |
30 | return cmd
31 | }
32 |
--------------------------------------------------------------------------------
/pkg/cmd/invite/invite_test.go:
--------------------------------------------------------------------------------
1 | package invite
2 |
--------------------------------------------------------------------------------
/pkg/cmd/login/login_test.go:
--------------------------------------------------------------------------------
1 | package login
2 |
--------------------------------------------------------------------------------
/pkg/cmd/logout/logout.go:
--------------------------------------------------------------------------------
1 | // Package logout is for the logout command
2 | package logout
3 |
4 | import (
5 | "fmt"
6 | "strings"
7 |
8 | "github.com/hashicorp/go-multierror"
9 | "github.com/spf13/cobra"
10 |
11 | "github.com/brevdev/brev-cli/pkg/cmd/cmderrors"
12 | breverrors "github.com/brevdev/brev-cli/pkg/errors"
13 | )
14 |
15 | type LogoutOptions struct {
16 | auth Auth
17 | store LogoutStore
18 | }
19 |
20 | type Auth interface {
21 | Logout() error
22 | }
23 |
24 | type LogoutStore interface {
25 | ClearDefaultOrganization() error
26 | GetCurrentWorkspaceID() (string, error)
27 | }
28 |
29 | func NewCmdLogout(auth Auth, store LogoutStore) *cobra.Command {
30 | opts := LogoutOptions{
31 | auth: auth,
32 | store: store,
33 | }
34 |
35 | cmd := &cobra.Command{
36 | Annotations: map[string]string{"housekeeping": ""},
37 | Use: "logout",
38 | DisableFlagsInUseLine: true,
39 | Short: "Log out of brev",
40 | Long: "Log out of brev by deleting the credential file",
41 | Example: "brev logout",
42 | Args: cmderrors.TransformToValidationError(cobra.NoArgs),
43 | RunE: func(cmd *cobra.Command, args []string) error {
44 | err := opts.RunLogout()
45 | if err != nil {
46 | return breverrors.WrapAndTrace(err)
47 | }
48 | return nil
49 | },
50 | }
51 | return cmd
52 | }
53 |
54 | func (o *LogoutOptions) RunLogout() error {
55 | workspaceID, err := o.store.GetCurrentWorkspaceID()
56 | if err != nil {
57 | return breverrors.WrapAndTrace(err)
58 | }
59 | if workspaceID != "" {
60 | return fmt.Errorf("can not logout of workspace")
61 | }
62 |
63 | // best effort
64 | var allErr error
65 | err = o.auth.Logout()
66 | if err != nil {
67 | if !strings.Contains(err.Error(), ".brev/credentials.json: no such file or directory") {
68 | allErr = multierror.Append(err)
69 | }
70 | }
71 |
72 | err = o.store.ClearDefaultOrganization()
73 | if err != nil {
74 | allErr = multierror.Append(err)
75 | }
76 |
77 | if allErr != nil {
78 | return breverrors.WrapAndTrace(allErr)
79 | }
80 | return nil
81 | }
82 |
--------------------------------------------------------------------------------
/pkg/cmd/ls/ls_test.go:
--------------------------------------------------------------------------------
1 | package ls
2 |
--------------------------------------------------------------------------------
/pkg/cmd/notebook/notebook.go:
--------------------------------------------------------------------------------
1 | package notebook
2 |
3 | import (
4 | "github.com/brevdev/brev-cli/pkg/cmd/hello"
5 | "github.com/brevdev/brev-cli/pkg/cmd/portforward"
6 | "github.com/brevdev/brev-cli/pkg/cmd/util"
7 | "github.com/brevdev/brev-cli/pkg/entity"
8 | breverrors "github.com/brevdev/brev-cli/pkg/errors"
9 | "github.com/brevdev/brev-cli/pkg/terminal"
10 | "github.com/fatih/color"
11 | "github.com/spf13/cobra"
12 | )
13 |
14 | var (
15 | notebookLong = "Open a notebook on your Brev machine"
16 | notebookExample = "brev notebook "
17 | )
18 |
19 | type NotebookStore interface {
20 | portforward.PortforwardStore
21 | }
22 |
23 | type WorkspaceResult struct {
24 | Workspace *entity.Workspace // Replace with the actual type of workspace returned by GetUserWorkspaceByNameOrIDErr
25 | Err error
26 | }
27 |
28 | func NewCmdNotebook(store NotebookStore, _ *terminal.Terminal) *cobra.Command {
29 | cmd := &cobra.Command{
30 | Use: "notebook",
31 | Short: "Open a notebook on your Brev machine",
32 | Long: notebookLong,
33 | Example: notebookExample,
34 | Args: cobra.ExactArgs(2),
35 | RunE: func(cmd *cobra.Command, args []string) error {
36 | // Channel to get the result of the network call
37 | resultCh := make(chan *WorkspaceResult)
38 |
39 | // Start the network call in a goroutine
40 | go func() {
41 | workspace, err := util.GetUserWorkspaceByNameOrIDErr(store, args[0])
42 | resultCh <- &WorkspaceResult{Workspace: workspace, Err: err}
43 | }()
44 |
45 | // Type out the checking message
46 | hello.TypeItToMeUnskippable27("Checking to make sure the workspace is running...")
47 |
48 | // Wait for the network call to finish
49 | result := <-resultCh
50 |
51 | if result.Err != nil {
52 | return breverrors.WrapAndTrace(result.Err)
53 | }
54 |
55 | // Check if the workspace is running
56 | if result.Workspace.Status != "RUNNING" {
57 | hello.TypeItToMeUnskippable27("The workspace is not running. Please ensure it's in the running state before proceeding.")
58 | return breverrors.WorkspaceNotRunning{Status: result.Workspace.Status}
59 | }
60 |
61 | urlType := color.New(color.FgCyan, color.Bold).SprintFunc()
62 | warningType := color.New(color.FgBlack, color.Bold, color.BgCyan).SprintFunc()
63 |
64 | hello.TypeItToMeUnskippable("\n" + warningType(" Please keep this terminal open 🤙 "))
65 |
66 | hello.TypeItToMeUnskippable27("\nClick here to go to your Jupyter notebook:\n\t 👉" + urlType("http://localhost:8888") + "👈\n\n\n")
67 |
68 | // Port forward on 8888
69 | err2 := portforward.RunPortforward(store, args[0], "8888:8888", false)
70 | if err2 != nil {
71 | return breverrors.WrapAndTrace(err2)
72 | }
73 |
74 | // Print out a link for the user
75 | hello.TypeItToMeUnskippable27("Your notebook is accessible at: http://localhost:8888")
76 |
77 | return nil
78 | },
79 | }
80 |
81 | return cmd
82 | }
83 |
--------------------------------------------------------------------------------
/pkg/cmd/notebook/notebook_test.go:
--------------------------------------------------------------------------------
1 | package notebook
2 |
--------------------------------------------------------------------------------
/pkg/cmd/ollama/ollama_test.go:
--------------------------------------------------------------------------------
1 | package ollama
2 |
--------------------------------------------------------------------------------
/pkg/cmd/ollama/ollamauiverb.yaml:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/brevdev/brev-cli/2fb523a140c1f2b720968511c32b5ff80e4eaee9/pkg/cmd/ollama/ollamauiverb.yaml
--------------------------------------------------------------------------------
/pkg/cmd/ollama/ollamaverb.yaml:
--------------------------------------------------------------------------------
1 | build:
2 | python_version: "3.10"
3 | cuda: 12.0.1
4 | python_packages:
5 | - jupyterlab
6 | run:
7 | - curl -fsSL https://ollama.com/install.sh | sh
8 | user:
9 | shell: zsh
10 | authorized_keys_path: /home/ubuntu/.ssh/authorized_keys
11 | ports:
12 | - "2222:22"
13 | - "8000:8000"
14 | services:
15 | - name: ollama-server
16 | entrypoint: OLLAMA_HOST=0.0.0.0 ollama serve
17 | ports:
18 | - 127.0.0.1:11434:11434
19 |
--------------------------------------------------------------------------------
/pkg/cmd/open/open_test.go:
--------------------------------------------------------------------------------
1 | package open
2 |
--------------------------------------------------------------------------------
/pkg/cmd/org/ls.go:
--------------------------------------------------------------------------------
1 | package org
2 |
3 | import (
4 | "github.com/brevdev/brev-cli/pkg/cmd/cmderrors"
5 | "github.com/brevdev/brev-cli/pkg/cmdcontext"
6 | breverrors "github.com/brevdev/brev-cli/pkg/errors"
7 | "github.com/brevdev/brev-cli/pkg/terminal"
8 |
9 | "github.com/spf13/cobra"
10 | )
11 |
12 | func NewCmdOrgLs(t *terminal.Terminal, orgcmdStore OrgCmdStore) *cobra.Command {
13 | cmd := &cobra.Command{
14 | Annotations: map[string]string{"context": ""},
15 | Use: "ls",
16 | Short: "List your organizations",
17 | Long: `List your organizations, your current org will be prefixed
18 | with * and highlighted with green`,
19 | Example: `brev org ls`,
20 | PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
21 | err := cmdcontext.InvokeParentPersistentPreRun(cmd, args)
22 | if err != nil {
23 | return breverrors.WrapAndTrace(err)
24 | }
25 | return nil
26 | },
27 | Args: cmderrors.TransformToValidationError(cobra.NoArgs),
28 | // ValidArgs: []string{"new", "ls"},
29 | RunE: func(cmd *cobra.Command, args []string) error {
30 | err := RunOrgs(t, orgcmdStore)
31 | if err != nil {
32 | return breverrors.WrapAndTrace(err)
33 | }
34 | return nil
35 | },
36 | }
37 | return cmd
38 | }
39 |
--------------------------------------------------------------------------------
/pkg/cmd/org/org_test.go:
--------------------------------------------------------------------------------
1 | package org
2 |
--------------------------------------------------------------------------------
/pkg/cmd/org/set.go:
--------------------------------------------------------------------------------
1 | package org
2 |
3 | import (
4 | "fmt"
5 |
6 | "github.com/brevdev/brev-cli/pkg/cmd/cmderrors"
7 | "github.com/brevdev/brev-cli/pkg/cmd/completions"
8 | "github.com/brevdev/brev-cli/pkg/cmdcontext"
9 | breverrors "github.com/brevdev/brev-cli/pkg/errors"
10 | "github.com/brevdev/brev-cli/pkg/store"
11 | "github.com/brevdev/brev-cli/pkg/terminal"
12 |
13 | "github.com/spf13/cobra"
14 | )
15 |
16 | func NewCmdOrgSet(t *terminal.Terminal, orgcmdStore OrgCmdStore, noorgcmdStore OrgCmdStore) *cobra.Command {
17 | var showAll bool
18 | var org string
19 |
20 | cmd := &cobra.Command{
21 | Annotations: map[string]string{"context": ""},
22 | Use: "set",
23 | Short: "Set active org",
24 | Long: "Set your organization to view, open, create workspaces etc",
25 | Example: `
26 | brev org set
27 | `,
28 | PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
29 | err := cmdcontext.InvokeParentPersistentPreRun(cmd, args)
30 | if err != nil {
31 | return breverrors.WrapAndTrace(err)
32 | }
33 |
34 | return nil
35 | },
36 | Args: cmderrors.TransformToValidationError(cobra.MinimumNArgs(1)),
37 | // ValidArgs: []string{"new", "ls"},
38 | RunE: func(cmd *cobra.Command, args []string) error {
39 | err := set(args[0], orgcmdStore, t)
40 | if err != nil {
41 | return breverrors.WrapAndTrace(err)
42 | }
43 | return nil
44 | },
45 | }
46 |
47 | cmd.Flags().StringVarP(&org, "org", "o", "", "organization (will override active org)")
48 | err := cmd.RegisterFlagCompletionFunc("org", completions.GetOrgsNameCompletionHandler(noorgcmdStore, t))
49 | if err != nil {
50 | breverrors.GetDefaultErrorReporter().ReportError(breverrors.WrapAndTrace(err))
51 | fmt.Print(breverrors.WrapAndTrace(err))
52 | }
53 |
54 | cmd.Flags().BoolVar(&showAll, "all", false, "show all workspaces in org")
55 |
56 | return cmd
57 | }
58 |
59 | func set(orgName string, setStore OrgCmdStore, t *terminal.Terminal) error {
60 | workspaceID, err := setStore.GetCurrentWorkspaceID()
61 | if err != nil {
62 | return breverrors.WrapAndTrace(err)
63 | }
64 | fmt.Println(workspaceID)
65 |
66 | if workspaceID != "" {
67 | return breverrors.NewValidationError("can not set orgs in a workspace")
68 | }
69 | orgs, err := setStore.GetOrganizations(&store.GetOrganizationsOptions{Name: orgName})
70 | if err != nil {
71 | return breverrors.WrapAndTrace(err)
72 | }
73 | if len(orgs) == 0 {
74 | return breverrors.NewValidationError(fmt.Sprintf("no orgs exist with name %s", orgName))
75 | } else if len(orgs) > 1 {
76 | return breverrors.NewValidationError(fmt.Sprintf("more than one org exist with name %s", orgName))
77 | }
78 |
79 | org := orgs[0]
80 |
81 | err = setStore.SetDefaultOrganization(&org)
82 | if err != nil {
83 | return breverrors.WrapAndTrace(err)
84 | }
85 | t.Vprintf("Org %s is now active 🤙\n", t.Green(org.Name))
86 |
87 | // Print workspaces within org
88 |
89 | return nil
90 | }
91 |
--------------------------------------------------------------------------------
/pkg/cmd/paths/paths.go:
--------------------------------------------------------------------------------
1 | package paths
2 |
3 | import (
4 | "fmt"
5 | "io/ioutil"
6 | // breverrors "github.com/brevdev/brev-cli/pkg/errors"
7 | )
8 |
9 | func GetVsCodePaths() []string {
10 | fi, err := ioutil.ReadDir("/home/brev/.vscode-server/bin")
11 | if err != nil {
12 | return []string{}
13 | }
14 | paths := []string{}
15 | for _, f := range fi {
16 | paths = append(paths, fmt.Sprintf("/home/brev/.vscode-server/bin/%s/bin/remote-cli", f.Name()))
17 | }
18 | return paths
19 | }
20 |
--------------------------------------------------------------------------------
/pkg/cmd/paths/paths_test.go:
--------------------------------------------------------------------------------
1 | package paths
2 |
3 | import (
4 | "testing"
5 | )
6 |
7 | func TestGetVsCodePaths(_ *testing.T) {
8 | _ = GetVsCodePaths()
9 | }
10 |
--------------------------------------------------------------------------------
/pkg/cmd/portforward/portforward_test.go:
--------------------------------------------------------------------------------
1 | package portforward
2 |
--------------------------------------------------------------------------------
/pkg/cmd/profile/profile_test.go:
--------------------------------------------------------------------------------
1 | package profile
2 |
--------------------------------------------------------------------------------
/pkg/cmd/proxy/proxy_test.go:
--------------------------------------------------------------------------------
1 | package proxy
2 |
3 | import (
4 | "testing"
5 |
6 | "github.com/hashicorp/go-version"
7 | "github.com/stretchr/testify/assert"
8 | )
9 |
10 | func TestVersionParsing(t *testing.T) {
11 | _, err := version.NewVersion("abadfjladsf")
12 | assert.NotNil(t, err)
13 | }
14 |
--------------------------------------------------------------------------------
/pkg/cmd/recreate/doc.md:
--------------------------------------------------------------------------------
1 | # Re Create Workspace by name or ID.
2 |
3 | ## SYNOPSIS
4 |
5 | ```
6 | brev recreate [ Workspace Name or ID... ]
7 | ```
8 |
9 | ## DESCRIPTION
10 |
11 | recreate a workspace is equivalent to running the following commands:
12 |
13 | ```
14 | brev delete payments-fronted
15 | brev start payments-frontend
16 | ```
17 |
18 | This command has the effect of updating the base image of a workspace to the
19 | latest. If your workspace has a git remote source, the workspace will start
20 | with a fresh copy of the remote source and run the workspace setupscript.
21 |
22 | ## EXAMPLE
23 |
24 | recreate a workspace with the name `naive-pubsub`
25 |
26 | ```
27 | $ brev recreate payments-frontend
28 | Starting hard reset 🤙 This can take a couple of minutes.
29 |
30 | Deleting workspace - naive-pubsub.
31 | Workspace is starting. This can take up to 2 minutes the first time.
32 | name naive-pubsub
33 | template v7nd45zsc Admin
34 | resource class 4x16
35 | workspace group brev-test-brevtenant-cluster
36 | You can safely ctrl+c to exit
37 | ⢿ workspace is deploying
38 | Your workspace is ready!
39 |
40 | SSH into your machine:
41 | ssh naive-pubsub-uq0x
42 | ```
43 |
44 | ## SEE ALSO
45 |
46 | TODO
47 |
--------------------------------------------------------------------------------
/pkg/cmd/refresh/refresh_test.go:
--------------------------------------------------------------------------------
1 | package refresh
2 |
--------------------------------------------------------------------------------
/pkg/cmd/reset/doc.md:
--------------------------------------------------------------------------------
1 | ## SYNOPSIS
2 |
3 | ```
4 | brev reset [ Workspace Name or ID... ]
5 | ```
6 |
7 | ## DESCRIPTION
8 |
9 | reset a workspace will stop a workspace, then start a workspace, perserving
10 | files in `/home/brev/workspace/`. This will have the effect of rerunning your
11 | setupscript in a newley created workspace with no changes made to it, and
12 | replacing your workspace with that.
13 |
14 | ## EXAMPLE
15 |
16 | reset a workspace with the name `payments-frontend`
17 |
18 | ```
19 | $ brev reset payments-frontend
20 | Workspace payments-frontend is resetting.
21 | Note: this can take a few seconds. Run 'brev ls' to check status
22 |
23 | ```
24 |
25 | ## SEE ALSO
26 |
27 | TODO
28 |
--------------------------------------------------------------------------------
/pkg/cmd/reset/reset_test.go:
--------------------------------------------------------------------------------
1 | package reset
2 |
--------------------------------------------------------------------------------
/pkg/cmd/runtasks/doc.md:
--------------------------------------------------------------------------------
1 | ##### Synopsis
2 |
3 | ```
4 | brev run-tasks -d
5 | ```
6 |
7 | ##### Description
8 |
9 | In order for brev to connect to workspaces, there needs to be background daemons
10 | running to manage some things on your local machines environment. Currently, the
11 | one that is being launched by run-tasks is an ssh config file configuration
12 | daemon that periodically udpates a ssh config file with connection information
13 | in order to access you workspaces.
14 |
15 | This command has to be run at every boot, see [Configuring SSH Proxy Daemon at Boot](https://docs.brev.dev/howto/configure-ssh-proxy-daemon-at-boot/) to
16 | configure this command to be run at boot.
17 |
18 | This command is set to be deprecated in favor of `brev configure`.
19 |
20 | ##### Examples
21 |
22 | to run tasks in the background
23 |
24 | ```
25 | $ brev run-tasks -d
26 | PID File: /home/f/.brev/task_daemon.pid
27 | Log File: /home/f/.brev/task_daemon.log
28 | ```
29 |
30 | to run tasks in the foreground
31 |
32 | ```
33 | $ brev run-tasks
34 | 2022/07/11 15:28:44 creating new ssh config
35 | 2022/07/11 15:28:48 creating new ssh config
36 |
37 | ```
38 |
39 | ##### See Also
40 |
41 | - [Configuring SSH Proxy Daemon at Boot](https://docs.brev.dev/howto/configure-ssh-proxy-daemon-at-boot/)
42 | -TODO brev configure docs
43 |
--------------------------------------------------------------------------------
/pkg/cmd/runtasks/runtasks.go:
--------------------------------------------------------------------------------
1 | package runtasks
2 |
3 | import (
4 | _ "embed"
5 |
6 | "github.com/brevdev/brev-cli/pkg/cmd/cmderrors"
7 | "github.com/brevdev/brev-cli/pkg/entity"
8 | breverrors "github.com/brevdev/brev-cli/pkg/errors"
9 | "github.com/brevdev/brev-cli/pkg/ssh"
10 | "github.com/brevdev/brev-cli/pkg/tasks"
11 | "github.com/brevdev/brev-cli/pkg/terminal"
12 | "github.com/spf13/cobra"
13 | stripmd "github.com/writeas/go-strip-markdown"
14 | )
15 |
16 | //go:embed doc.md
17 | var long string
18 |
19 | func NewCmdRunTasks(t *terminal.Terminal, store RunTasksStore) *cobra.Command {
20 | var detached bool
21 | // would be nice to have a way to pass in a list of tasks to run instead of the default
22 | var runRemoteCMD bool
23 |
24 | cmd := &cobra.Command{
25 | Annotations: map[string]string{"housekeeping": ""},
26 | Use: "run-tasks",
27 | DisableFlagsInUseLine: true,
28 | Short: "Run background tasks for brev",
29 | Long: stripmd.Strip(long),
30 | Example: "brev run-tasks -d",
31 | Args: cmderrors.TransformToValidationError(cobra.ExactArgs(0)),
32 | RunE: func(cmd *cobra.Command, args []string) error {
33 | err := RunTasks(t, store, detached)
34 | if err != nil {
35 | return breverrors.WrapAndTrace(err)
36 | }
37 | return nil
38 | },
39 | }
40 |
41 | cmd.Flags().BoolVarP(&detached, "detached", "d", false, "run the command in the background instead of blocking the shell")
42 | cmd.Flags().BoolVarP(&runRemoteCMD, "run-remote-cmd", "r", true, "run the command on the instance to cd into ws default dir")
43 | return cmd
44 | }
45 |
46 | type RunTasksStore interface {
47 | ssh.ConfigUpdaterStore
48 | ssh.SSHConfigurerV2Store
49 | tasks.RunTaskAsDaemonStore
50 | GetCurrentUser() (*entity.User, error)
51 | GetCurrentUserKeys() (*entity.UserKeys, error)
52 | }
53 |
54 | func RunTasks(_ *terminal.Terminal, store RunTasksStore, detached bool) error {
55 | ts, err := getDefaultTasks(store)
56 | if err != nil {
57 | return breverrors.WrapAndTrace(err)
58 | }
59 | if detached {
60 | err := tasks.RunTaskAsDaemon(ts, store)
61 | if err != nil {
62 | return breverrors.WrapAndTrace(err)
63 | }
64 | } else {
65 | err := tasks.RunTasks(ts)
66 | if err != nil {
67 | return breverrors.WrapAndTrace(err)
68 | }
69 | }
70 | return nil
71 | }
72 |
73 | func getDefaultTasks(store RunTasksStore) ([]tasks.Task, error) {
74 | configs, err := ssh.GetSSHConfigs(store)
75 | if err != nil {
76 | return nil, breverrors.WrapAndTrace(err)
77 | }
78 |
79 | // get private key and set here
80 | keys, err := store.GetCurrentUserKeys()
81 | if err != nil {
82 | return nil, breverrors.WrapAndTrace(err)
83 | }
84 |
85 | cu := ssh.NewConfigUpdater(store, configs, keys.PrivateKey)
86 |
87 | return []tasks.Task{cu}, nil
88 | }
89 |
--------------------------------------------------------------------------------
/pkg/cmd/runtasks/runtasks_test.go:
--------------------------------------------------------------------------------
1 | package runtasks
2 |
--------------------------------------------------------------------------------
/pkg/cmd/secret/secret_test.go:
--------------------------------------------------------------------------------
1 | package secret
2 |
--------------------------------------------------------------------------------
/pkg/cmd/set/set.go:
--------------------------------------------------------------------------------
1 | // Package set is for the set command
2 | package set
3 |
4 | import (
5 | "fmt"
6 |
7 | "github.com/brevdev/brev-cli/pkg/cmd/cmderrors"
8 | "github.com/brevdev/brev-cli/pkg/cmd/completions"
9 | "github.com/brevdev/brev-cli/pkg/cmdcontext"
10 | "github.com/brevdev/brev-cli/pkg/entity"
11 | "github.com/brevdev/brev-cli/pkg/store"
12 | "github.com/brevdev/brev-cli/pkg/terminal"
13 | "github.com/spf13/cobra"
14 |
15 | breverrors "github.com/brevdev/brev-cli/pkg/errors"
16 | )
17 |
18 | type SetStore interface {
19 | completions.CompletionStore
20 | SetDefaultOrganization(org *entity.Organization) error
21 | GetOrganizations(options *store.GetOrganizationsOptions) ([]entity.Organization, error)
22 | GetServerSockFile() string
23 | GetCurrentWorkspaceID() (string, error)
24 | }
25 |
26 | func NewCmdSet(t *terminal.Terminal, loginSetStore SetStore, noLoginSetStore SetStore) *cobra.Command {
27 | cmd := &cobra.Command{
28 | Annotations: map[string]string{"context": ""},
29 | Use: "set",
30 | Short: "Set active org (helps with completion)",
31 | Long: "Set your organization to view, open, create instances etc",
32 | Example: `brev set `,
33 | Args: cmderrors.TransformToValidationError(cobra.MinimumNArgs(1)),
34 | ValidArgsFunction: completions.GetOrgsNameCompletionHandler(noLoginSetStore, t),
35 | PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
36 | err := cmdcontext.InvokeParentPersistentPreRun(cmd, args)
37 | if err != nil {
38 | return breverrors.WrapAndTrace(err)
39 | }
40 | return nil
41 | },
42 | RunE: func(cmd *cobra.Command, args []string) error {
43 | err := set(args[0], loginSetStore)
44 | if err != nil {
45 | return breverrors.WrapAndTrace(err)
46 | }
47 | return nil
48 | },
49 | }
50 |
51 | return cmd
52 | }
53 |
54 | func set(orgName string, setStore SetStore) error {
55 | workspaceID, err := setStore.GetCurrentWorkspaceID()
56 | if err != nil {
57 | return breverrors.WrapAndTrace(err)
58 | }
59 | if workspaceID != "" {
60 | return fmt.Errorf("can not set orgs in a workspace")
61 | }
62 | orgs, err := setStore.GetOrganizations(&store.GetOrganizationsOptions{Name: orgName})
63 | if err != nil {
64 | return breverrors.WrapAndTrace(err)
65 | }
66 | if len(orgs) == 0 {
67 | return fmt.Errorf("no orgs exist with name %s", orgName)
68 | } else if len(orgs) > 1 {
69 | return fmt.Errorf("more than one org exist with name %s", orgName)
70 | }
71 |
72 | org := orgs[0]
73 |
74 | err = setStore.SetDefaultOrganization(&org)
75 | if err != nil {
76 | return breverrors.WrapAndTrace(err)
77 | }
78 |
79 | // Print workspaces within org
80 |
81 | return nil
82 | }
83 |
--------------------------------------------------------------------------------
/pkg/cmd/set/set_test.go:
--------------------------------------------------------------------------------
1 | package set
2 |
--------------------------------------------------------------------------------
/pkg/cmd/setupworkspace/setupworkspace.go:
--------------------------------------------------------------------------------
1 | package setupworkspace
2 |
3 | import (
4 | "fmt"
5 |
6 | "github.com/brevdev/brev-cli/pkg/entity"
7 | breverrors "github.com/brevdev/brev-cli/pkg/errors"
8 | "github.com/brevdev/brev-cli/pkg/featureflag"
9 | "github.com/brevdev/brev-cli/pkg/setupworkspace"
10 | "github.com/brevdev/brev-cli/pkg/store"
11 | "github.com/spf13/cobra"
12 | )
13 |
14 | type SetupWorkspaceStore interface {
15 | GetSetupParams() (*store.SetupParamsV0, error)
16 | WriteSetupScript(script string) error
17 | GetSetupScriptPath() string
18 | GetCurrentUser() (*entity.User, error)
19 | GetCurrentWorkspaceID() (string, error)
20 | }
21 |
22 | const Name = "setupworkspace"
23 |
24 | // Internal command for setting up workspace // v1 similar to k8s post-start script
25 | func NewCmdSetupWorkspace(store SetupWorkspaceStore) *cobra.Command {
26 | var forceEnableSetup bool
27 | cmd := &cobra.Command{
28 | Annotations: map[string]string{"hidden": ""},
29 | Use: Name,
30 | RunE: func(cmd *cobra.Command, args []string) error {
31 | breverrors.GetDefaultErrorReporter().AddTag("command", Name)
32 | _, err := store.GetCurrentWorkspaceID() // do this to error reporting
33 | if err != nil {
34 | return breverrors.WrapAndTrace(err)
35 | }
36 | fmt.Println("setting up instance")
37 |
38 | params, err := store.GetSetupParams()
39 | if err != nil {
40 | return breverrors.WrapAndTrace(err)
41 | }
42 |
43 | if !featureflag.IsDev() {
44 | _, err = store.GetCurrentUser() // do this to set error user reporting
45 | if err != nil {
46 | fmt.Println(err)
47 | if !params.DisableSetup {
48 | breverrors.GetDefaultErrorReporter().ReportError(breverrors.Wrap(err, "setup continued"))
49 | }
50 | }
51 | }
52 |
53 | if !forceEnableSetup && params.DisableSetup {
54 | fmt.Printf("WARNING: setup script not running [params.DisableSetup=%v, forceEnableSetup=%v]", params.DisableSetup, forceEnableSetup)
55 | return nil
56 | }
57 |
58 | err = setupworkspace.SetupWorkspace(params)
59 | if err != nil {
60 | return breverrors.WrapAndTrace(err)
61 | }
62 | fmt.Println("done setting up instance")
63 | return nil
64 | },
65 | }
66 | cmd.PersistentFlags().BoolVar(&forceEnableSetup, "force-enable", false, "force the setup script to run despite params")
67 |
68 | return cmd
69 | }
70 |
--------------------------------------------------------------------------------
/pkg/cmd/shell/shell_test.go:
--------------------------------------------------------------------------------
1 | package shell
2 |
--------------------------------------------------------------------------------
/pkg/cmd/sshkeys/sshkeys.go:
--------------------------------------------------------------------------------
1 | // Package sshkeys gets your public ssh key to add to github/gitlab
2 | package sshkeys
3 |
4 | import (
5 | "github.com/brevdev/brev-cli/pkg/cmdcontext"
6 | "github.com/brevdev/brev-cli/pkg/entity"
7 | breverrors "github.com/brevdev/brev-cli/pkg/errors"
8 | "github.com/brevdev/brev-cli/pkg/terminal"
9 |
10 | "github.com/spf13/cobra"
11 | )
12 |
13 | type SSHKeyStore interface {
14 | GetCurrentUser() (*entity.User, error)
15 | }
16 |
17 | func NewCmdSSHKeys(t *terminal.Terminal, sshKeyStore SSHKeyStore) *cobra.Command {
18 | cmd := &cobra.Command{
19 | Annotations: map[string]string{"housekeeping": ""},
20 | Use: "ssh-key",
21 | Short: "Get your pulic SSH-Key",
22 | Long: "Get your pulic SSH-Key to add to pull and push from your git repository.",
23 | Example: `brev ssh-key`,
24 | PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
25 | err := cmdcontext.InvokeParentPersistentPreRun(cmd, args)
26 | if err != nil {
27 | return breverrors.WrapAndTrace(err)
28 | }
29 |
30 | return nil
31 | },
32 | Args: cobra.NoArgs,
33 | RunE: func(cmd *cobra.Command, args []string) error {
34 | user, err := sshKeyStore.GetCurrentUser()
35 | if err != nil {
36 | return breverrors.WrapAndTrace(err)
37 | }
38 | DisplaySSHKeys(t, user.PublicKey)
39 | return nil
40 | },
41 | }
42 |
43 | return cmd
44 | }
45 |
46 | func DisplaySSHKeys(t *terminal.Terminal, publicKey string) {
47 | t.Vprintf(publicKey)
48 | t.Print("\n")
49 | t.Eprintf(t.Yellow("Copy 👆 and add it to your git provider:\n"))
50 | t.Eprintf(t.Yellow("\tGithub: https://github.com/settings/keys\n"))
51 | t.Eprintf(t.Yellow("\tGitlab: https://gitlab.com/-/profile/keys\n"))
52 | t.Eprintf(t.Yellow("Check authentication by starting a new instance\n"))
53 | t.Eprintf(t.Yellow("\tbrev start --empty --name test-ssh && brev delete test-ssh\n"))
54 | }
55 |
--------------------------------------------------------------------------------
/pkg/cmd/sshkeys/sshkeys_test.go:
--------------------------------------------------------------------------------
1 | package sshkeys
2 |
--------------------------------------------------------------------------------
/pkg/cmd/start/start_test.go:
--------------------------------------------------------------------------------
1 | package start
2 |
3 | import (
4 | "testing"
5 |
6 | "github.com/brevdev/brev-cli/pkg/entity"
7 | "github.com/brevdev/brev-cli/pkg/terminal"
8 | "github.com/stretchr/testify/assert"
9 | )
10 |
11 | func TestMakeNewWorkspaceFromURL(t *testing.T) {
12 | gitTruth := "github.com:brevdev/brev-cli.git"
13 | nameTruth := "brev-cli"
14 |
15 | wksTruth := NewWorkspace{
16 | Name: nameTruth,
17 | GitRepo: gitTruth,
18 | }
19 |
20 | naked := "https://github.com/brevdev/brev-cli"
21 | res := MakeNewWorkspaceFromURL(naked)
22 | if !assert.Equal(t, wksTruth, res) {
23 | return
24 | }
25 |
26 | http := "http://github.com/brevdev/brev-cli.git"
27 | res = MakeNewWorkspaceFromURL(http)
28 | if !assert.Equal(t, wksTruth, res) {
29 | return
30 | }
31 |
32 | https := "https://github.com/brevdev/brev-cli.git"
33 | res = MakeNewWorkspaceFromURL(https)
34 | if !assert.Equal(t, wksTruth, res) {
35 | return
36 | }
37 |
38 | ssh := "git@github.com:brevdev/brev-cli.git"
39 | res = MakeNewWorkspaceFromURL(ssh)
40 | if !assert.Equal(t, wksTruth, res) {
41 | return
42 | }
43 | }
44 |
45 | func Test_DisplayBC(t *testing.T) {
46 | term := terminal.New()
47 | displayConnectBreadCrumb(term, &entity.Workspace{
48 | ID: "123456789",
49 | Name: "my-name",
50 | WorkspaceGroupID: "",
51 | OrganizationID: "",
52 | WorkspaceClassID: "",
53 | CreatedByUserID: "",
54 | DNS: "",
55 | Status: "",
56 | Password: "",
57 | GitRepo: "",
58 | Version: "",
59 | WorkspaceTemplate: entity.WorkspaceTemplate{},
60 | NetworkID: "",
61 | })
62 | }
63 |
--------------------------------------------------------------------------------
/pkg/cmd/status/status.go:
--------------------------------------------------------------------------------
1 | package status
2 |
3 | import (
4 | "github.com/brevdev/brev-cli/pkg/cmd/util"
5 | "github.com/brevdev/brev-cli/pkg/entity"
6 | "github.com/brevdev/brev-cli/pkg/store"
7 | "github.com/brevdev/brev-cli/pkg/terminal"
8 | "github.com/spf13/cobra"
9 | )
10 |
11 | var (
12 | createLong = "Create a new Brev machine"
13 | createExample = `
14 | brev create
15 | `
16 | // instanceTypes = []string{"p4d.24xlarge", "p3.2xlarge", "p3.8xlarge", "p3.16xlarge", "p3dn.24xlarge", "p2.xlarge", "p2.8xlarge", "p2.16xlarge", "g5.xlarge", "g5.2xlarge", "g5.4xlarge", "g5.8xlarge", "g5.16xlarge", "g5.12xlarge", "g5.24xlarge", "g5.48xlarge", "g5g.xlarge", "g5g.2xlarge", "g5g.4xlarge", "g5g.8xlarge", "g5g.16xlarge", "g5g.metal", "g4dn.xlarge", "g4dn.2xlarge", "g4dn.4xlarge", "g4dn.8xlarge", "g4dn.16xlarge", "g4dn.12xlarge", "g4dn.metal", "g4ad.xlarge", "g4ad.2xlarge", "g4ad.4xlarge", "g4ad.8xlarge", "g4ad.16xlarge", "g3s.xlarge", "g3.4xlarge", "g3.8xlarge", "g3.16xlarge"}
17 | )
18 |
19 | type StatusStore interface {
20 | util.GetWorkspaceByNameOrIDErrStore
21 | GetActiveOrganizationOrDefault() (*entity.Organization, error)
22 | GetCurrentUser() (*entity.User, error)
23 | GetWorkspace(workspaceID string) (*entity.Workspace, error)
24 | GetCurrentWorkspaceID() (string, error)
25 | CreateWorkspace(organizationID string, options *store.CreateWorkspacesOptions) (*entity.Workspace, error)
26 | }
27 |
28 | func NewCmdStatus(t *terminal.Terminal, statusStore StatusStore) *cobra.Command {
29 | cmd := &cobra.Command{
30 | Annotations: map[string]string{"workspace": ""},
31 | Use: "status",
32 | DisableFlagsInUseLine: true,
33 | Short: "About this instance",
34 | Long: createLong,
35 | Example: createExample,
36 | RunE: func(cmd *cobra.Command, args []string) error {
37 | runShowStatus(t, statusStore)
38 | return nil
39 | },
40 | }
41 | return cmd
42 | }
43 |
44 | func runShowStatus(t *terminal.Terminal, statusStore StatusStore) {
45 | terminal.DisplayBrevLogo(t)
46 | t.Vprintf("\n")
47 | wsID, err := statusStore.GetCurrentWorkspaceID()
48 | if err != nil {
49 | t.Vprintf("\n Error: %s", t.Red(err.Error()))
50 | return
51 | }
52 | ws, err := statusStore.GetWorkspace(wsID)
53 | if err != nil {
54 | t.Vprintf("\n Error: %s", t.Red(err.Error()))
55 | return
56 | }
57 |
58 | t.Vprintf("\nYou're on instance %s", t.Yellow(ws.Name))
59 | t.Vprintf("\n\tID: %s", t.Yellow(ws.ID))
60 | t.Vprintf("\n\tMachine: %s", t.Yellow(util.GetInstanceString(*ws)))
61 | }
62 |
--------------------------------------------------------------------------------
/pkg/cmd/status/status_test.go:
--------------------------------------------------------------------------------
1 | package status
2 |
--------------------------------------------------------------------------------
/pkg/cmd/stop/doc.md:
--------------------------------------------------------------------------------
1 | ## EXAMPLES
2 |
3 | stop multiple workspaces
4 |
5 | ```
6 | $ brev stop brev-deploy naive-pubsub bar euler54 merge-json
7 | Workspace brev-deploy is stopping.
8 | Note: this can take a few seconds. Run 'brev ls' to check status
9 | Workspace naive-pubsub is stopping.
10 | Note: this can take a few seconds. Run 'brev ls' to check status
11 | Workspace bar is stopping.
12 | Note: this can take a few seconds. Run 'brev ls' to check status
13 | Workspace euler54 is stopping.
14 | Note: this can take a few seconds. Run 'brev ls' to check status
15 | Workspace merge-json is stopping.
16 | Note: this can take a few seconds. Run 'brev ls' to check status
17 | ```
18 |
--------------------------------------------------------------------------------
/pkg/cmd/stop/stop_test.go:
--------------------------------------------------------------------------------
1 | package stop
2 |
3 | import (
4 | "testing"
5 | )
6 |
7 | func TestStopWorkspaceSelf(_ *testing.T) {
8 | // err := stopThisWorkspace(nil, nil)
9 | // assert.Nil(t, err)
10 | }
11 |
--------------------------------------------------------------------------------
/pkg/cmd/test/test.go:
--------------------------------------------------------------------------------
1 | package test
2 |
3 | import (
4 | "fmt"
5 |
6 | "github.com/brevdev/brev-cli/pkg/autostartconf"
7 | "github.com/brevdev/brev-cli/pkg/cmd/completions"
8 | "github.com/brevdev/brev-cli/pkg/entity"
9 | "github.com/brevdev/brev-cli/pkg/store"
10 | "github.com/brevdev/brev-cli/pkg/terminal"
11 | "github.com/brevdev/brev-cli/pkg/util"
12 |
13 | "github.com/spf13/cobra"
14 | )
15 |
16 | var (
17 | startLong = "[internal] test"
18 | startExample = "[internal] test"
19 | )
20 |
21 | type TestStore interface {
22 | completions.CompletionStore
23 | ResetWorkspace(workspaceID string) (*entity.Workspace, error)
24 | GetAllWorkspaces(options *store.GetWorkspacesOptions) ([]entity.Workspace, error)
25 | GetWorkspaces(organizationID string, options *store.GetWorkspacesOptions) ([]entity.Workspace, error)
26 | GetActiveOrganizationOrDefault() (*entity.Organization, error)
27 | GetCurrentUser() (*entity.User, error)
28 | GetWorkspace(id string) (*entity.Workspace, error)
29 | GetWorkspaceMetaData(workspaceID string) (*entity.WorkspaceMetaData, error)
30 | CopyBin(targetBin string) error
31 | GetSetupScriptContentsByURL(url string) (string, error)
32 | UpdateUser(userID string, updatedUser *entity.UpdateUser) (*entity.User, error)
33 | }
34 |
35 | type ServiceMeshStore interface {
36 | autostartconf.AutoStartStore
37 | GetWorkspace(workspaceID string) (*entity.Workspace, error)
38 | }
39 |
40 | func NewCmdTest(_ *terminal.Terminal, _ TestStore) *cobra.Command {
41 | cmd := &cobra.Command{
42 | Annotations: map[string]string{"devonly": ""},
43 | Use: "test",
44 | DisableFlagsInUseLine: true,
45 | Short: "[internal] Test random stuff.",
46 | Long: startLong,
47 | Example: startExample,
48 | // Args: cmderrors.TransformToValidationError(cobra.MinimumNArgs(1)),
49 | RunE: func(cmd *cobra.Command, args []string) error {
50 | // fmt.Printf("NAME ID URL SOMETHING ELSE")
51 | // hello.TypeItToMe("\n\n\n")
52 | // hello.TypeItToMe("👆 this is the name of your environment (which you can use to open the environment)")
53 | // time.Sleep(1 * time.Second)
54 | // fmt.Printf("\332K\r")
55 | // fmt.Println(" ")
56 | // hello.TypeItToMe(" 👆 you can expose your localhost to this public URL")
57 | // time.Sleep(1 * time.Second)
58 | // fmt.Printf("\332K\r")
59 | // fmt.Printf("bye world")
60 | // fmt.Printf("bye world")
61 |
62 | // s := t.Yellow("\n\nCould you please install the following VSCode extension? %s", t.Green("ms-vscode-remote.remote-ssh"))
63 | // s += "\nDo that then run " + t.Yellow("brev hello") + " to resume this walk-through\n"
64 | // // s += "Here's a video of me installing the VS Code extension 👉 " + ""
65 | // hello.TypeItToMe(s)
66 |
67 | res := util.DoesPathExist("/Users/naderkhalil/brev-cli")
68 | // res := util.DoesPathExist("/home/brev/workspace")
69 | fmt.Println(res)
70 |
71 | return nil
72 | },
73 | }
74 |
75 | return cmd
76 | }
77 |
--------------------------------------------------------------------------------
/pkg/cmd/test/test_test.go:
--------------------------------------------------------------------------------
1 | package test
2 |
--------------------------------------------------------------------------------
/pkg/cmd/util/util.go:
--------------------------------------------------------------------------------
1 | package util
2 |
3 | import (
4 | "fmt"
5 |
6 | "github.com/brevdev/brev-cli/pkg/entity"
7 | breverrors "github.com/brevdev/brev-cli/pkg/errors"
8 | "github.com/brevdev/brev-cli/pkg/store"
9 | )
10 |
11 | type GetWorkspaceByNameOrIDErrStore interface {
12 | GetActiveOrganizationOrDefault() (*entity.Organization, error)
13 | GetWorkspaceByNameOrID(orgID string, nameOrID string) ([]entity.Workspace, error)
14 | GetCurrentUser() (*entity.User, error)
15 | }
16 |
17 | func GetUserWorkspaceByNameOrIDErr(storeQ GetWorkspaceByNameOrIDErrStore, workspaceNameOrID string) (*entity.Workspace, error) {
18 | user, err := storeQ.GetCurrentUser()
19 | if err != nil {
20 | return nil, breverrors.WrapAndTrace(err)
21 | }
22 | org, err := storeQ.GetActiveOrganizationOrDefault()
23 | if err != nil {
24 | return nil, breverrors.WrapAndTrace(err)
25 | }
26 | workspaces, err := storeQ.GetWorkspaceByNameOrID(org.ID, workspaceNameOrID)
27 | if err != nil {
28 | return nil, breverrors.WrapAndTrace(err)
29 | }
30 |
31 | workspaces = store.FilterForUserWorkspaces(workspaces, user.ID)
32 | if len(workspaces) == 0 {
33 | return nil, breverrors.NewValidationError(fmt.Sprintf("instance with id/name %s not found", workspaceNameOrID))
34 | }
35 | return &workspaces[0], nil
36 | }
37 |
38 | func GetAnyWorkspaceByIDOrNameInActiveOrgErr(storeQ GetWorkspaceByNameOrIDErrStore, workspaceNameOrID string) (*entity.Workspace, error) {
39 | org, err := storeQ.GetActiveOrganizationOrDefault()
40 | if err != nil {
41 | return nil, breverrors.WrapAndTrace(err)
42 | }
43 | workspaces, err := storeQ.GetWorkspaceByNameOrID(org.ID, workspaceNameOrID)
44 | if err != nil {
45 | return nil, breverrors.WrapAndTrace(err)
46 | }
47 |
48 | if len(workspaces) == 0 {
49 | return nil, breverrors.NewValidationError(fmt.Sprintf("instance with id/name %s not found", workspaceNameOrID))
50 | }
51 | if len(workspaces) > 1 {
52 | workspaces = store.FilterNonFailedWorkspaces(workspaces)
53 | if len(workspaces) == 0 {
54 | return nil, breverrors.NewValidationError(fmt.Sprintf("instance with id/name %s is a failed workspace", workspaceNameOrID))
55 | }
56 | if len(workspaces) > 1 {
57 | return nil, breverrors.NewValidationError(fmt.Sprintf("multiple instances found with id/name %s", workspaceNameOrID))
58 | }
59 | }
60 | return &workspaces[0], nil
61 | }
62 |
63 | type MakeWorkspaceWithMetaStore interface {
64 | GetWorkspaceMetaData(workspaceID string) (*entity.WorkspaceMetaData, error)
65 | }
66 |
67 | func GetClassIDString(classID string) string {
68 | // switch statement on class ID
69 | switch classID {
70 | case "2x2":
71 | return "2 cpu | 2 gb ram"
72 | case "2x4":
73 | return "2 cpu | 4 gb ram"
74 | case "2x8":
75 | return "2 cpu | 8 gb ram"
76 | case "4x16":
77 | return "4 cpu | 16 gb ram"
78 | case "8x32":
79 | return "8 cpu | 32 gb ram"
80 | case "16x32":
81 | return "16 cpu | 32 gb ram"
82 | default:
83 | return classID
84 |
85 | }
86 | }
87 |
88 | func GetInstanceString(w entity.Workspace) string {
89 | var instanceString string
90 | if w.WorkspaceClassID != "" {
91 | instanceString = GetClassIDString(w.WorkspaceClassID)
92 | } else {
93 | instanceString = w.InstanceType + " (gpu)"
94 | }
95 | return instanceString
96 | }
97 |
--------------------------------------------------------------------------------
/pkg/cmd/version/version.go:
--------------------------------------------------------------------------------
1 | package version
2 |
3 | var Version = ""
4 |
--------------------------------------------------------------------------------
/pkg/cmd/version/version_test.go:
--------------------------------------------------------------------------------
1 | package version
2 |
3 | import (
4 | "testing"
5 | )
6 |
7 | func TestBuildVersionString(_ *testing.T) {
8 | // func TestBuildVersionString(t *testing.T) {
9 | // terminalStub := &terminal.Terminal{}
10 |
11 | // want := "unknown"
12 | // got, err := buildVersionString(terminalStub)
13 |
14 | // if want != got || err != nil {
15 | // t.Errorf(`buildVersionString() = %q, %v, want match for %#q, nil`, got, err, want)
16 | // }
17 | }
18 |
--------------------------------------------------------------------------------
/pkg/cmd/workspacegroups/workspacegroups.go:
--------------------------------------------------------------------------------
1 | package workspacegroups
2 |
3 | import (
4 | "os"
5 |
6 | "github.com/jedib0t/go-pretty/v6/table"
7 | "github.com/spf13/cobra"
8 |
9 | "github.com/brevdev/brev-cli/pkg/entity"
10 | breverrors "github.com/brevdev/brev-cli/pkg/errors"
11 | "github.com/brevdev/brev-cli/pkg/terminal"
12 | )
13 |
14 | type WorkspaceGroupsStore interface {
15 | GetWorkspaceGroups(organizationID string) ([]entity.WorkspaceGroup, error)
16 | GetActiveOrganizationOrDefault() (*entity.Organization, error)
17 | }
18 |
19 | func NewCmdWorkspaceGroups(t *terminal.Terminal, store WorkspaceGroupsStore) *cobra.Command {
20 | cmd := &cobra.Command{
21 | Use: "workspacegroups",
22 | DisableFlagsInUseLine: true,
23 | Short: "TODO",
24 | Long: "TODO",
25 | Example: "TODO",
26 | RunE: func(cmd *cobra.Command, args []string) error {
27 | err := RunWorkspaceGroups(t, args, store)
28 | if err != nil {
29 | return breverrors.WrapAndTrace(err)
30 | }
31 | return nil
32 | },
33 | }
34 | return cmd
35 | }
36 |
37 | func RunWorkspaceGroups(_ *terminal.Terminal, _ []string, store WorkspaceGroupsStore) error {
38 | org, err := store.GetActiveOrganizationOrDefault()
39 | if err != nil {
40 | return breverrors.WrapAndTrace(err)
41 | }
42 | wsgs, err := store.GetWorkspaceGroups(org.ID)
43 | if err != nil {
44 | return breverrors.WrapAndTrace(err)
45 | }
46 |
47 | ta := table.NewWriter()
48 | ta.SetOutputMirror(os.Stdout)
49 | ta.Style().Options = getBrevTableOptions()
50 | header := table.Row{"NAME", "PLATFORM ID", "PLATFORM TYPE"}
51 | ta.AppendHeader(header)
52 | for _, w := range wsgs {
53 | workspaceRow := []table.Row{{
54 | w.Name, w.PlatformID, w.Platform,
55 | }}
56 | ta.AppendRows(workspaceRow)
57 | }
58 | ta.Render()
59 | return nil
60 | }
61 |
62 | func getBrevTableOptions() table.Options {
63 | options := table.OptionsDefault
64 | options.DrawBorder = false
65 | options.SeparateColumns = false
66 | options.SeparateRows = false
67 | options.SeparateHeader = false
68 | return options
69 | }
70 |
--------------------------------------------------------------------------------
/pkg/cmd/writeconnectionevent/writeconnectionevent.go:
--------------------------------------------------------------------------------
1 | package writeconnectionevent
2 |
3 | import (
4 | "github.com/spf13/cobra"
5 |
6 | breverrors "github.com/brevdev/brev-cli/pkg/errors"
7 | "github.com/brevdev/brev-cli/pkg/terminal"
8 | )
9 |
10 | var (
11 | short = "TODO"
12 | long = "TODO"
13 | example = "TODO"
14 | )
15 |
16 | type writeConnectionEventStore interface {
17 | WriteConnectionEvent() error
18 | }
19 |
20 | func NewCmdwriteConnectionEvent(t *terminal.Terminal, store writeConnectionEventStore) *cobra.Command {
21 | cmd := &cobra.Command{
22 | Use: "write-connection-event",
23 | DisableFlagsInUseLine: true,
24 | Short: short,
25 | Long: long,
26 | Example: example,
27 | RunE: func(cmd *cobra.Command, args []string) error {
28 | err := RunWriteConnectionEvent(t, args, store)
29 | if err != nil {
30 | return breverrors.WrapAndTrace(err)
31 | }
32 | return nil
33 | },
34 | }
35 | return cmd
36 | }
37 |
38 | func RunWriteConnectionEvent(_ *terminal.Terminal, _ []string, store writeConnectionEventStore) error {
39 | err := store.WriteConnectionEvent()
40 | if err != nil {
41 | return breverrors.WrapAndTrace(err)
42 | }
43 | return nil
44 | }
45 |
--------------------------------------------------------------------------------
/pkg/cmd/writeconnectionevent/writeconnectionevent_test.go:
--------------------------------------------------------------------------------
1 | package writeconnectionevent
2 |
3 | import (
4 | "testing"
5 |
6 | "github.com/brevdev/brev-cli/pkg/store"
7 | "github.com/brevdev/brev-cli/pkg/terminal"
8 | "github.com/spf13/afero"
9 | )
10 |
11 | func TestRunWriteConnectionEvent(t *testing.T) {
12 | fs := afero.NewMemMapFs()
13 | type args struct {
14 | in0 *terminal.Terminal
15 | in1 []string
16 | store writeConnectionEventStore
17 | }
18 | tests := []struct {
19 | name string
20 | args args
21 | wantErr bool
22 | }{
23 | // TODO: Add test cases.
24 | {
25 | name: "write connection event",
26 | args: args{
27 | nil,
28 | []string{},
29 | store.NewBasicStore().WithFileSystem(fs),
30 | },
31 | wantErr: false,
32 | },
33 | }
34 | for _, tt := range tests {
35 | t.Run(tt.name, func(t *testing.T) {
36 | if err := RunWriteConnectionEvent(tt.args.in0, tt.args.in1, tt.args.store); (err != nil) != tt.wantErr {
37 | t.Errorf("RunWriteConnectionEvent() error = %v, wantErr %v", err, tt.wantErr)
38 | }
39 | })
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/pkg/cmdcontext/cmdcontext.go:
--------------------------------------------------------------------------------
1 | package cmdcontext
2 |
3 | import (
4 | breverrors "github.com/brevdev/brev-cli/pkg/errors"
5 | "github.com/spf13/cobra"
6 | )
7 |
8 | // InvokeParentPersistentPreRun executes the immediate parent command's
9 | // PersistentPreRunE and PersistentPreRun functions, in that order. If
10 | // an error is returned from PersistentPreRunE, it is immediately returned.
11 | //
12 | // TODO: reverse walk up command tree? would need to ensure no one parent is invoked multiple times.
13 | func InvokeParentPersistentPreRun(cmd *cobra.Command, args []string) error {
14 | parentCmd := cmd.Parent()
15 | if parentCmd == nil {
16 | return nil
17 | }
18 |
19 | var err error
20 |
21 | // Invoke PersistentPreRunE, returning an error if one occurs
22 | // If no error is returned, proceed with PersistentPreRun
23 | parentPersistentPreRunE := parentCmd.PersistentPreRunE
24 | if parentPersistentPreRunE != nil {
25 | err = parentPersistentPreRunE(parentCmd, args)
26 | }
27 | if err != nil {
28 | return breverrors.WrapAndTrace(err)
29 | }
30 |
31 | // Invoke PersistentPreRun
32 | parentPersistentPreRun := parentCmd.PersistentPreRun
33 | if parentPersistentPreRun != nil {
34 | parentPersistentPreRun(parentCmd, args)
35 | }
36 |
37 | return nil
38 | }
39 |
--------------------------------------------------------------------------------
/pkg/cmdcontext/cmdwriter.go:
--------------------------------------------------------------------------------
1 | package cmdcontext
2 |
3 | // NoopWriter is an implementation of the standard Writer which takes no action
4 | // upon being asked to write.
5 | type NoopWriter struct{}
6 |
--------------------------------------------------------------------------------
/pkg/config/config.go:
--------------------------------------------------------------------------------
1 | package config
2 |
3 | import (
4 | "os"
5 | )
6 |
7 | type EnvVarName string // should be caps with underscore
8 |
9 | const (
10 | brevAPIURL EnvVarName = "BREV_API_URL"
11 | coordURL EnvVarName = "BREV_COORD_URL"
12 | version EnvVarName = "VERSION"
13 | clusterID EnvVarName = "DEFAULT_CLUSTER_ID"
14 | defaultWorkspaceClass EnvVarName = "DEFAULT_WORKSPACE_CLASS"
15 | defaultWorkspaceTemplate EnvVarName = "DEFAULT_WORKSPACE_TEMPLATE"
16 | sentryURL EnvVarName = "DEFAULT_SENTRY_URL"
17 | debugHTTP EnvVarName = "DEBUG_HTTP"
18 | ollamaAPIURL EnvVarName = "OLLAMA_API_URL"
19 | )
20 |
21 | var ConsoleBaseURL = "https://console.brev.dev"
22 |
23 | type ConstantsConfig struct{}
24 |
25 | func NewConstants() *ConstantsConfig {
26 | return &ConstantsConfig{}
27 | }
28 |
29 | func (c ConstantsConfig) GetBrevAPIURl() string {
30 | return getEnvOrDefault(brevAPIURL, "https://brevapi.us-west-2-prod.control-plane.brev.dev")
31 | }
32 |
33 | func (c ConstantsConfig) GetOllamaAPIURL() string {
34 | return getEnvOrDefault(ollamaAPIURL, "https://registry.ollama.ai")
35 | }
36 |
37 | func (c ConstantsConfig) GetDefaultClusterID() string {
38 | return getEnvOrDefault(clusterID, "devplane-brev-1")
39 | }
40 |
41 | func (c ConstantsConfig) GetDefaultWorkspaceClass() string {
42 | return getEnvOrDefault(defaultWorkspaceClass, "")
43 | }
44 |
45 | func (c ConstantsConfig) GetDefaultWorkspaceTemplate() string {
46 | // "test-template-aws"
47 | return getEnvOrDefault(defaultWorkspaceTemplate, "")
48 | }
49 |
50 | func (c ConstantsConfig) GetDebugHTTP() bool {
51 | return getEnvOrDefault(debugHTTP, "") != ""
52 | }
53 |
54 | func getEnvOrDefault(envVarName EnvVarName, defaultVal string) string {
55 | val := os.Getenv(string(envVarName))
56 | if val == "" {
57 | return defaultVal
58 | }
59 | return val
60 | }
61 |
62 | var GlobalConfig = NewConstants()
63 |
64 | type EnvVarConfig struct {
65 | ConstantsConfig
66 | }
67 |
68 | type FileConfig struct {
69 | EnvVarConfig
70 | }
71 |
72 | type FlagsConfig struct {
73 | FileConfig
74 | }
75 |
76 | type InitConfig interface{}
77 |
78 | type AllConfig interface {
79 | InitConfig
80 | GetBrevAPIURl() string
81 | GetVersion() string
82 | GetDefaultClusterID() string
83 | }
84 |
--------------------------------------------------------------------------------
/pkg/entity/generic/entitygeneric.go:
--------------------------------------------------------------------------------
1 | package generic
2 |
3 | import (
4 | "github.com/brevdev/brev-cli/pkg/entity"
5 | orderedmap "github.com/wk8/go-ordered-map/v2"
6 | )
7 |
8 | func MakeVirtualProjectMap() *orderedmap.OrderedMap[string, map[string][]entity.Workspace] {
9 | vpMap := orderedmap.New[string, map[string][]entity.Workspace]()
10 | return vpMap
11 | }
12 |
--------------------------------------------------------------------------------
/pkg/entity/virtualproject/virtualproject.go:
--------------------------------------------------------------------------------
1 | package virtualproject
2 |
3 | import (
4 | "github.com/brevdev/brev-cli/pkg/entity"
5 | "github.com/brevdev/brev-cli/pkg/entity/generic"
6 | )
7 |
8 | type VirtualProject struct {
9 | Name string
10 | GitURL string
11 | WorkspacesByUser map[string][]entity.Workspace
12 | }
13 |
14 | func NewVirtualProjects(workspaces []entity.Workspace) []VirtualProject {
15 | gitRepoWorkspaceMap := generic.MakeVirtualProjectMap()
16 | for _, w := range workspaces {
17 | if _, ok := gitRepoWorkspaceMap.Get(w.GitRepo); !ok {
18 | gitRepoWorkspaceMap.Set(w.GitRepo, make(map[string][]entity.Workspace))
19 | }
20 | m, ok := gitRepoWorkspaceMap.Get(w.GitRepo) // [w.GitRepo][w.CreatedByUserID] =
21 | if !ok {
22 | panic("no")
23 | }
24 | m[w.CreatedByUserID] = append(m[w.CreatedByUserID], w)
25 | gitRepoWorkspaceMap.Set(w.GitRepo, m)
26 | }
27 | var projects []VirtualProject
28 | for pair := gitRepoWorkspaceMap.Oldest(); pair != nil; pair = pair.Next() {
29 | key, ok := GetFirstKeyMap(pair.Value)
30 | if !ok {
31 | continue
32 | }
33 |
34 | if len(pair.Value[key]) == 0 {
35 | continue
36 | }
37 |
38 | projectName := pair.Value[key][0].Name // TODO this is the SUPER hacky unexpected behavior part
39 | projects = append(projects, VirtualProject{Name: projectName, GitURL: pair.Key, WorkspacesByUser: pair.Value})
40 | }
41 | return projects
42 | }
43 |
44 | func GetFirstKeyMap(strMap map[string][]entity.Workspace) (string, bool) {
45 | for k := range strMap {
46 | return k, true
47 | }
48 | return "", false
49 | }
50 |
51 | func (v VirtualProject) GetUserWorkspaces(userID string) []entity.Workspace {
52 | return v.WorkspacesByUser[userID]
53 | }
54 |
55 | func (v VirtualProject) GetUniqueUserCount() int {
56 | return len(v.WorkspacesByUser)
57 | }
58 |
--------------------------------------------------------------------------------
/pkg/entity/virtualproject/virtualproject_test.go:
--------------------------------------------------------------------------------
1 | package virtualproject
2 |
3 | import (
4 | "testing"
5 |
6 | "github.com/brevdev/brev-cli/pkg/entity"
7 | "github.com/stretchr/testify/assert"
8 | )
9 |
10 | func TestNewVirtualProjectOne(t *testing.T) {
11 | ps := NewVirtualProjects([]entity.Workspace{{
12 | ID: "1",
13 | Name: "hi",
14 | GitRepo: "git://hi",
15 | CreatedByUserID: "me",
16 | }})
17 | if !assert.Len(t, ps, 1) {
18 | return
19 | }
20 | assert.Equal(t, ps[0].Name, "hi")
21 | assert.Equal(t, ps[0].GitURL, "git://hi")
22 | assert.Equal(t, 1, ps[0].GetUniqueUserCount())
23 | assert.Len(t, ps[0].GetUserWorkspaces("me"), 1)
24 | }
25 |
26 | func TestNewVirtualProjectNone(t *testing.T) {
27 | ps := NewVirtualProjects([]entity.Workspace{})
28 | assert.Len(t, ps, 0)
29 | }
30 |
31 | func TestNewVirtualProjectTwoDiffRepo(t *testing.T) {
32 | ps := NewVirtualProjects([]entity.Workspace{{
33 | ID: "1",
34 | Name: "hi",
35 | GitRepo: "git://hi",
36 | CreatedByUserID: "me",
37 | }, {
38 | ID: "2",
39 | Name: "bye",
40 | GitRepo: "git://bye",
41 | CreatedByUserID: "other",
42 | }})
43 | if !assert.Len(t, ps, 2) {
44 | return
45 | }
46 | assert.Equal(t, ps[0].Name, "hi")
47 | assert.Equal(t, ps[0].GitURL, "git://hi")
48 | assert.Equal(t, 1, ps[0].GetUniqueUserCount())
49 | assert.Len(t, ps[0].GetUserWorkspaces("me"), 1)
50 |
51 | assert.Equal(t, ps[1].Name, "bye")
52 | assert.Equal(t, ps[1].GitURL, "git://bye")
53 | assert.Equal(t, 1, ps[1].GetUniqueUserCount())
54 | assert.Len(t, ps[1].GetUserWorkspaces("other"), 1)
55 | }
56 |
57 | func TestNewVirtualProjectTwoSameRepo(t *testing.T) {
58 | ps := NewVirtualProjects([]entity.Workspace{{
59 | ID: "1",
60 | Name: "hi",
61 | GitRepo: "git://hi",
62 | CreatedByUserID: "me",
63 | }, {
64 | ID: "2",
65 | Name: "hi",
66 | GitRepo: "git://hi",
67 | CreatedByUserID: "other",
68 | }})
69 | if !assert.Len(t, ps, 1) {
70 | return
71 | }
72 | assert.Equal(t, ps[0].Name, "hi")
73 | assert.Equal(t, ps[0].GitURL, "git://hi")
74 | assert.Equal(t, 2, ps[0].GetUniqueUserCount())
75 | assert.Len(t, ps[0].GetUserWorkspaces("me"), 1)
76 | assert.Len(t, ps[0].GetUserWorkspaces("other"), 1)
77 | }
78 |
79 | func TestNewVirtualProjectTwoSameRepoSameUser(t *testing.T) {
80 | ps := NewVirtualProjects([]entity.Workspace{{
81 | ID: "1",
82 | Name: "hi",
83 | GitRepo: "git://hi",
84 | CreatedByUserID: "me",
85 | }, {
86 | ID: "2",
87 | Name: "hi",
88 | GitRepo: "git://hi",
89 | CreatedByUserID: "me",
90 | }})
91 | if !assert.Len(t, ps, 1) {
92 | return
93 | }
94 | assert.Equal(t, ps[0].Name, "hi")
95 | assert.Equal(t, ps[0].GitURL, "git://hi")
96 | assert.Equal(t, 1, ps[0].GetUniqueUserCount())
97 | assert.Len(t, ps[0].GetUserWorkspaces("me"), 2)
98 | assert.Len(t, ps[0].GetUserWorkspaces("other"), 0)
99 | }
100 |
--------------------------------------------------------------------------------
/pkg/featureflag/featureflag.go:
--------------------------------------------------------------------------------
1 | package featureflag
2 |
3 | import (
4 | "strings"
5 |
6 | "github.com/brevdev/brev-cli/pkg/cmd/version"
7 | "github.com/brevdev/brev-cli/pkg/entity"
8 | "github.com/spf13/viper"
9 | )
10 |
11 | func IsDev() bool {
12 | if viper.IsSet("feature.dev") {
13 | return viper.GetBool("feature.dev")
14 | } else {
15 | return strings.HasPrefix(version.Version, "dev")
16 | }
17 | }
18 |
19 | func IsAdmin(userType entity.GlobalUserType) bool {
20 | if viper.IsSet("feature.not_admin") && viper.GetBool("feature.not_admin") {
21 | return false
22 | } else {
23 | return userType == "Admin"
24 | }
25 | }
26 |
27 | // use feature flag if not provided default true for admin but not others
28 |
29 | func DisableSSHProxyVersionCheck() bool {
30 | return viper.GetBool("feature.disable_ssh_proxy_version_check")
31 | }
32 |
33 | func ShowVersionOnRun() bool {
34 | return viper.GetBool("feature.show_version_on_run")
35 | }
36 |
37 | // todo set me via cli flag? this was meant to sort of like verbose but could be
38 | // removed in favor of something like that
39 | func Debug() bool {
40 | return viper.GetBool("feature.debug")
41 | }
42 |
43 | func LoadFeatureFlags(path string) error {
44 | viper.SetConfigName("config")
45 | viper.AddConfigPath("/etc/brev/")
46 | viper.AddConfigPath(path)
47 | viper.SetEnvPrefix("brev")
48 | viper.SetConfigType("yaml")
49 | viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
50 | viper.AutomaticEnv()
51 |
52 | _ = viper.ReadInConfig() // do not need to fail if can't find config file
53 |
54 | return nil
55 | }
56 |
--------------------------------------------------------------------------------
/pkg/featureflag/featureflag_test.go:
--------------------------------------------------------------------------------
1 | package featureflag
2 |
--------------------------------------------------------------------------------
/pkg/files/files_test.go:
--------------------------------------------------------------------------------
1 | package files
2 |
3 | // Basic imports
4 | import (
5 | "os"
6 | "testing"
7 |
8 | "github.com/stretchr/testify/suite"
9 | )
10 |
11 | // Define the suite, and absorb the built-in basic suite
12 | // functionality from testify - including a T() method which
13 | // returns the current testing context
14 | type filesTestSuite struct {
15 | suite.Suite
16 | }
17 |
18 | // All methods that begin with "Test" are run as tests within a
19 | // suite.
20 | func (s *filesTestSuite) TestGetUserSSHConfigPath() {
21 | home, _ := os.UserHomeDir()
22 | _, err := GetUserSSHConfigPath(home)
23 | s.Nil(err)
24 | }
25 |
26 | // In order for 'go test' to run this suite, we need to create
27 | // a normal test function and pass our suite to suite.Run
28 | func TestFiles(t *testing.T) {
29 | suite.Run(t, new(filesTestSuite))
30 | }
31 |
--------------------------------------------------------------------------------
/pkg/huproxyclient/huproxyclient.go:
--------------------------------------------------------------------------------
1 | package huproxyclient
2 |
3 | // https://github.com/google/huproxy/blob/master/huproxyclient/client.go
4 |
5 | import (
6 | "context"
7 | "crypto/tls"
8 | "fmt"
9 | "io"
10 | "io/ioutil"
11 | "net/http"
12 | "os"
13 | "time"
14 |
15 | "github.com/brevdev/brev-cli/pkg/entity"
16 | "github.com/brevdev/brev-cli/pkg/errors"
17 | "github.com/gorilla/websocket"
18 | log "github.com/sirupsen/logrus"
19 |
20 | huproxy "github.com/google/huproxy/lib"
21 | )
22 |
23 | var writeTimeout = 10 * time.Second
24 |
25 | type HubProxyStore interface {
26 | GetAuthTokens() (*entity.AuthTokens, error)
27 | GetCurrentWorkspaceGroupID() (string, error)
28 | }
29 |
30 | func dialError(url string, resp *http.Response, err error) {
31 | if resp != nil {
32 | extra := ""
33 | b, err1 := ioutil.ReadAll(resp.Body)
34 | if err1 != nil {
35 | log.Warningf("Failed to read HTTP body: %v", err1)
36 | }
37 | extra = "Body:\n" + string(b)
38 | log.Fatalf("%s: HTTP error: %d %s\n%s", err, resp.StatusCode, resp.Status, extra)
39 |
40 | }
41 | log.Fatalf("Dial to %q fail: %v", url, err)
42 | }
43 |
44 | func Run(url string, store HubProxyStore) error {
45 | ctx, cancel := context.WithCancel(context.Background())
46 | defer cancel()
47 |
48 | dialer := websocket.Dialer{}
49 | dialer.TLSClientConfig = new(tls.Config)
50 |
51 | head := map[string][]string{}
52 |
53 | token, err := store.GetAuthTokens()
54 | if err != nil {
55 | return errors.WrapAndTrace(err)
56 | }
57 |
58 | workspaceGroupID, err := store.GetCurrentWorkspaceGroupID()
59 | if err != nil {
60 | fmt.Printf("%v\n", err)
61 | }
62 | if workspaceGroupID != "" {
63 | head["X-Workspace-Group-ID"] = []string{workspaceGroupID}
64 | }
65 |
66 | head["Authorization"] = []string{
67 | "Bearer " + token.AccessToken,
68 | }
69 |
70 | conn, resp, err := dialer.Dial(url, head)
71 | if err != nil {
72 | dialError(url, resp, err)
73 | }
74 | defer resp.Body.Close() //nolint:errcheck // lazy to refactor
75 | defer conn.Close() //nolint:errcheck // lazy to refactor
76 |
77 | RunProxy(ctx, conn, cancel)
78 |
79 | if ctx.Err() != nil {
80 | return errors.WrapAndTrace(ctx.Err())
81 | }
82 | return nil
83 | }
84 |
85 | func RunProxy(ctx context.Context, conn *websocket.Conn, cancel context.CancelFunc) {
86 | // websocket -> stdout
87 | go func() {
88 | for {
89 | mt, r, err := conn.NextReader()
90 | if websocket.IsCloseError(err, websocket.CloseNormalClosure) {
91 | return
92 | }
93 | if err != nil {
94 | log.Warn("Workspace disconnect: may be from network failure or workspace was stopped/deleted")
95 | log.Fatal(err)
96 | }
97 | if mt != websocket.BinaryMessage {
98 | log.Fatal("non-binary websocket message received")
99 | }
100 | if _, err := io.Copy(os.Stdout, r); err != nil {
101 | log.Errorf("Reading from websocket: %v", err)
102 | cancel()
103 | }
104 | }
105 | }()
106 |
107 | // stdin -> websocket
108 | // TODO: NextWriter() seems to be broken.
109 | if err := huproxy.File2WS(ctx, cancel, os.Stdin, conn); err == io.EOF {
110 | if err1 := conn.WriteControl(websocket.CloseMessage,
111 | websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""),
112 | time.Now().Add(writeTimeout)); err1 == websocket.ErrCloseSent {
113 | _ = ""
114 | } else if err1 != nil {
115 | log.Errorf("Error sending 'close' message: %v", err1)
116 | }
117 | } else if err != nil {
118 | log.Errorf("reading from stdin: %v", err)
119 | cancel()
120 | }
121 | }
122 |
--------------------------------------------------------------------------------
/pkg/huproxyclient/huproxyclient_test.go:
--------------------------------------------------------------------------------
1 | package huproxyclient
2 |
--------------------------------------------------------------------------------
/pkg/ids/ids.go:
--------------------------------------------------------------------------------
1 | package ids
2 |
3 | import "github.com/brevdev/brev-cli/pkg/prefixid"
4 |
5 | type (
6 | CloudCredID prefixid.PrefixID
7 | InstanceID prefixid.PrefixID
8 | CloudProviderInstanceID prefixid.PrefixID
9 | CloudProviderID string
10 | )
11 |
12 | type HealthCheckID prefixid.PrefixID
13 |
14 | type CreditID prefixid.PrefixID
15 |
16 | type LimitID prefixid.PrefixID
17 |
--------------------------------------------------------------------------------
/pkg/mergeshells/templates/gatsby/gatsby:
--------------------------------------------------------------------------------
1 | # gatsby
2 | # dependencies: node npm-no-sudo
3 | # installing gatsby-cli
4 | npm install -g gatsby-cli
5 |
--------------------------------------------------------------------------------
/pkg/mergeshells/templates/golang/golang:
--------------------------------------------------------------------------------
1 | # golang
2 | # installing Golang
3 | (echo ""; echo "##### Golang ${version} #####"; echo "";)
4 | wget https://golang.org/dl/go${version}.linux-amd64.tar.gz
5 | sudo rm -rf /usr/local/go && sudo tar -C /usr/local -xzf go${version}.linux-amd64.tar.gz
6 | echo "" | sudo tee -a ~/.bashrc
7 | echo "export PATH=\$PATH:/usr/local/go/bin" | sudo tee -a ~/.bashrc
8 | echo "export PATH=\$PATH:$HOME/go/bin" | sudo tee -a ~/.bashrc
9 | source ~/.bashrc
10 | echo "" | sudo tee -a ~/.zshrc
11 | echo "export PATH=\$PATH:/usr/local/go/bin" | sudo tee -a ~/.zshrc
12 | echo "export PATH=\$PATH:$HOME/go/bin" | sudo tee -a ~/.zshrc
13 | source ~/.zshrc
14 | rm go${version}.linux-amd64.tar.gz
15 |
16 |
--------------------------------------------------------------------------------
/pkg/mergeshells/templates/node/14:
--------------------------------------------------------------------------------
1 | # node node
2 | # installing Node v14.x + npm
3 | (echo ""; echo "##### Node v14.x + npm #####"; echo "";)
4 | sudo apt install ca-certificates
5 | curl -fsSL https://deb.nodesource.com/setup_14.x | sudo -E bash -
6 | sudo apt-get install -y nodejs
7 | (echo ""; echo "##### Node v14.x + npm #####"; echo "";)
8 | sudo apt install ca-certificates
9 | curl -fsSL https://deb.nodesource.com/setup_14.x | sudo -E bash -
10 | sudo apt-get install -y nodejs
11 |
12 | # npm-no-sudo
13 | # dependencies: node
14 | # installing npm packages globally without sudo | modified from https://stackoverflow.com/questions/18088372/how-to-npm-install-global-not-as-root
15 | mkdir "${HOME}/.npm-packages"
16 | printf "prefix=${HOME}/.npm-packages" >> $HOME/.npmrc
17 | cat <> $HOME/.npmrc
17 | cat < { inherit system; } }:
2 |
3 | with pkgs;
4 | let
5 | pkgs = import (builtins.fetchGit {
6 | # Descriptive name to make the store path easier to identify
7 | name = "my-old-revision";
8 | url = "https://github.com/NixOS/nixpkgs/";
9 | ref = "refs/heads/nixpkgs-unstable";
10 | rev = "ff8b619cfecb98bb94ae49ca7ceca937923a75fa";
11 | # }) {};
12 | }) {
13 | inherit system;
14 | };
15 | flake-utils = {
16 | url = "github:numtide/flake-utils";
17 | inputs.nixpkgs.follows = "nixpkgs";
18 | };
19 | olderVersionOfGolangci-lint = pkgs.golangci-lint;
20 | # system = builtins.currentSystem;
21 | in
22 | mkShell {
23 | nativeBuildInputs = [
24 | go_1_19
25 | gopls
26 | tmux
27 | gofumpt
28 | olderVersionOfGolangci-lint
29 | gosec
30 | delve
31 | go-tools
32 | gotests
33 | gomodifytags
34 | ];
35 | # pkgs.system="x86_64-linux";
36 | }
37 | # )
--------------------------------------------------------------------------------
/tools/tools.go:
--------------------------------------------------------------------------------
1 | //go:build tools
2 | // +build tools
3 |
4 | package tools
5 |
6 | // Manage tool dependencies via go.mod.
7 | //
8 | // https://github.com/golang/go/wiki/Modules#how-can-i-track-tool-dependencies-for-a-module
9 | // https://github.com/golang/go/issues/25922
10 | import (
11 | _ "github.com/golangci/golangci-lint/cmd/golangci-lint"
12 | _ "github.com/goreleaser/goreleaser"
13 | _ "mvdan.cc/gofumpt"
14 | )
15 |
--------------------------------------------------------------------------------