Skip to content

Commit bf56ea5

Browse files
Timna BrownTimna Brown
authored andcommitted
feat: browser login, org/repo picker, Copilot AI, arm64 Docker fix
- Add devopster login via gh/az/glab CLI delegation with auto browser open, OSC 8 clickable links, and device-code flow that works inside containers - Add login status, login all, login logout subcommands - Add src/auth/mod.rs: persistent token store in ~/.config/devopster/tokens.json - Add src/ai/mod.rs: GitHub Copilot integration for topic/description suggestions - Rewrite devopster init: live org/repo picker from gh/az/glab, scoped_repos selection, copilot_enabled prompt, generates full config - Add scoped_repos + copilot_enabled to AppConfig with scope_to_config filter - Fix Dockerfile: --platform linux/arm64 and dynamic ARCH for glab install - Fix make setup: single build then run to prevent double-compile OOM - Add scripts/setup.sh and scripts/setup.ps1 - Rewrite Makefile with OS detection, tab indentation, help target - Update README to single make setup getting-started flow
1 parent 887f93d commit bf56ea5

18 files changed

Lines changed: 1444 additions & 25 deletions

File tree

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ anyhow = "1.0"
1616
async-trait = "0.1"
1717
base64 = "0.22"
1818
clap = { version = "4.5", features = ["derive", "env"] }
19+
dirs = "5.0"
1920
reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls"] }
2021
serde = { version = "1.0", features = ["derive"] }
2122
serde_json = "1.0"

Dockerfile

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,56 @@
1-
FROM rust:1.85-bookworm AS base
1+
FROM --platform=linux/arm64 rust:1.85-bookworm AS base
22

33
RUN rustup component add clippy rustfmt
44

5+
# ── System packages ──────────────────────────────────────────────────────────
56
RUN apt-get update \
67
&& apt-get install -y --no-install-recommends \
78
build-essential \
89
ca-certificates \
10+
curl \
911
git \
12+
gnupg \
13+
lsb-release \
1014
make \
1115
pkg-config \
1216
&& rm -rf /var/lib/apt/lists/*
1317

18+
# ── GitHub CLI (gh) ──────────────────────────────────────────────────────────
19+
RUN curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg \
20+
-o /usr/share/keyrings/githubcli-archive-keyring.gpg \
21+
&& chmod go+r /usr/share/keyrings/githubcli-archive-keyring.gpg \
22+
&& echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" \
23+
> /etc/apt/sources.list.d/github-cli.list \
24+
&& apt-get update \
25+
&& apt-get install -y --no-install-recommends gh \
26+
&& rm -rf /var/lib/apt/lists/*
27+
28+
# ── Azure CLI (az) ────────────────────────────────────────────────────────────
29+
RUN curl -sL https://aka.ms/InstallAzureCLIDeb | bash \
30+
&& rm -rf /var/lib/apt/lists/*
31+
32+
# ── GitLab CLI (glab) ─────────────────────────────────────────────────────────
33+
RUN ARCH=$(dpkg --print-architecture) \
34+
&& GLAB_VERSION=$(curl -fsSL https://gitlab.com/api/v4/projects/34675721/releases \
35+
| grep -o '"tag_name":"[^"]*"' | head -1 | cut -d'"' -f4 | sed 's/^v//') \
36+
&& curl -fsSL "https://gitlab.com/gitlab-org/cli/-/releases/v${GLAB_VERSION}/downloads/glab_${GLAB_VERSION}_linux_${ARCH}.deb" \
37+
-o /tmp/glab.deb \
38+
&& dpkg -i /tmp/glab.deb \
39+
&& rm /tmp/glab.deb
40+
1441
WORKDIR /app
1542
COPY . .
1643
RUN cargo fetch
1744

45+
# ── Build & install the binary ────────────────────────────────────────────────
46+
# `cargo install` places `devopster` in ~/.cargo/bin which is on PATH in the
47+
# official Rust image, so `devopster <cmd>` works directly after `make setup`.
48+
RUN cargo install --path . --locked
49+
1850
FROM base AS test
1951
RUN cargo test
2052

2153
FROM base AS dev
22-
CMD ["sleep", "infinity"]
54+
# Binary is already installed from base — start a shell so the user can run
55+
# `devopster login github` and any other command right away.
56+
CMD ["bash"]

Makefile

Lines changed: 55 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,74 @@
1-
.PHONY: setup build test lint fmt run container-build container-test
1+
.PHONY: setup build test lint fmt \
2+
container-build container-test container-run \
3+
login-github login-azure login-gitlab auth-status \
4+
help
25

6+
OS := $(shell uname -s 2>/dev/null || echo Windows)
7+
8+
## setup: Full automated setup -- installs Docker if needed, builds the image, opens a shell
39
setup:
4-
cargo fetch
10+
ifeq ($(OS),Darwin)
11+
@echo "==> Detected macOS"
12+
@chmod +x ./scripts/setup.sh && ./scripts/setup.sh
13+
else ifeq ($(OS),Linux)
14+
@echo "==> Detected Linux"
15+
@chmod +x ./scripts/setup.sh && ./scripts/setup.sh
16+
else
17+
@echo "==> Detected Windows"
18+
@powershell -ExecutionPolicy Bypass -File scripts\\setup.ps1
19+
endif
520

21+
## build: Compile the devopster binary
622
build:
723
cargo build
824

25+
## test: Run all tests
926
test:
1027
cargo test
1128

29+
## lint: Run clippy
1230
lint:
1331
cargo clippy --all-targets --all-features -- -D warnings
1432

33+
## fmt: Format all Rust source files
1534
fmt:
1635
cargo fmt --all
1736

18-
run:
19-
cargo run -- $(ARGS)
37+
## login-github: Sign in to GitHub via browser
38+
login-github:
39+
cargo run -- login github
40+
41+
## login-azure: Sign in to Azure DevOps via browser
42+
login-azure:
43+
cargo run -- login azure-devops
44+
45+
## login-gitlab: Sign in to GitLab via browser
46+
login-gitlab:
47+
cargo run -- login gitlab
2048

49+
## auth-status: Show authentication status for all providers
50+
auth-status:
51+
cargo run -- login status
52+
53+
## container-build: Build the dev container image
2154
container-build:
22-
docker build -t devopster-cli-dev -f .devcontainer/Dockerfile .
55+
docker build --platform linux/arm64 --target dev -t devopster-cli-dev .
2356

57+
## container-test: Run tests inside the container
2458
container-test:
25-
docker build -t devopster-cli-ci .
26-
docker run --rm devopster-cli-ci make test
59+
docker build --platform linux/arm64 -t devopster-cli-ci .
60+
docker run --rm --platform linux/arm64 devopster-cli-ci cargo test
61+
62+
## container-run: Open a shell inside the container with host credentials mounted
63+
container-run:
64+
docker build --platform linux/arm64 --target dev -t devopster-cli-dev .
65+
docker run --rm -it --platform linux/arm64 \
66+
-v "$(HOME)/.config/devopster:/root/.config/devopster" \
67+
-v "$(PWD):/app" \
68+
-w /app \
69+
devopster-cli-dev \
70+
$(if $(ARGS),$(ARGS),bash)
71+
72+
## help: Show available make targets
73+
help:
74+
@grep -E '^##' Makefile | sed 's/## / /'

README.md

Lines changed: 49 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,18 @@ The repository now includes:
3131

3232
## Container-First Workflow
3333

34-
This project is designed to be developed inside a container.
34+
This project is designed to run inside a container. **The only required tool on your host machine is `make`**, everything else including Docker is installed automatically by `make setup`.
35+
36+
### What `make setup` installs
37+
38+
| Tool | Where | How |
39+
|---|---|---|
40+
| Homebrew | macOS only, if missing | Official install script |
41+
| Docker Desktop | macOS | `brew install --cask docker` |
42+
| Docker Engine | Debian/Ubuntu | Official apt repository |
43+
| `gh`, `az`, `glab`, Rust, `devopster` | Inside the container | Dockerfile |
44+
45+
> On macOS, after Docker Desktop is installed for the first time you need to start it from Applications, then re-run `make setup`.
3546
3647
### VS Code Dev Container
3748

@@ -43,18 +54,48 @@ This project is designed to be developed inside a container.
4354
### Local Commands
4455

4556
```bash
57+
# First time on a new machine -- installs Docker if needed, builds the image,
58+
# and drops you into the container shell where devopster is ready to use:
4659
make setup
47-
make build
48-
make test
49-
make run ARGS="stats"
50-
make container-build
51-
make container-test
60+
61+
# Inside the container:
62+
devopster init
63+
devopster repo list
5264
```
5365

66+
> To reopen the container shell after exiting, run `make setup` again or `make container-run`.
67+
5468
## Example Commands
5569

70+
After `make setup` the `devopster` binary is on your `$PATH` and these commands work directly:
71+
72+
| Command | Purpose |
73+
|---|---|
74+
| `devopster init` | Create `devopster-config.yaml` and interactively sign in to a provider |
75+
| `devopster init --no-login` | Create `devopster-config.yaml` only, skip the sign-in prompt |
76+
| `devopster login <github\|azure-devops\|gitlab>` | Sign in to that provider via browser, uses `gh`, `az`, or `glab` CLI |
77+
| `devopster login all` | Sign in to all three providers sequentially |
78+
| `devopster login status` | Show authentication status for all providers |
79+
| `devopster login logout <provider>` | Remove stored credentials for a provider |
80+
| `devopster repo list` | List all repositories in the configured organization |
81+
| `devopster repo list --topic rust` | Filter repositories by topic |
82+
| `devopster repo audit` | Audit repos for missing description, topics, license, and default branch |
83+
| `devopster repo scaffold --name <name> --template <template>` | Create a new repository from a template defined in config |
84+
| `devopster repo sync` | Push files from `.github/` to all repositories |
85+
| `devopster catalog generate` | Export a JSON catalog of all repositories |
86+
| `devopster topics align` | Add missing template topics to every matching repository |
87+
| `devopster stats` | Print a summary: provider, org, and total repository count |
88+
89+
### Quick start inside the container
90+
5691
```bash
92+
# 1. build the image and install devopster
93+
make setup
94+
95+
# 2. init: creates devopster-config.yaml and asks which provider to sign in to
5796
devopster init
97+
98+
# 3. run any command
5899
devopster repo list
59100
devopster repo audit
60101
devopster repo scaffold --name sample-repo --template azure-overview
@@ -63,6 +104,8 @@ devopster topics align
63104
devopster stats
64105
```
65106

107+
> To skip the sign-in prompt: `devopster init --no-login`
108+
66109
## Configuration
67110

68111
Start from the example file:

devopster-config.example.yaml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,16 @@
11
provider: github
22
organization: MicrosoftCloudEssentials-LearningHub
33

4+
# Limit devopster commands to specific repositories.
5+
# Remove or leave empty to target all repositories in the organization.
6+
# scoped_repos:
7+
# - my-service-repo
8+
# - platform-infra
9+
10+
# Enable GitHub Copilot-assisted suggestions for repos missing topics or descriptions.
11+
# Requires a Copilot subscription. Set during `devopster init` or add manually.
12+
# copilot_enabled: true
13+
414
github:
515
api_url: https://api.github.com
616
token_env: GITHUB_TOKEN

scripts/setup.ps1

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# devopster setup for Windows
2+
# Requires PowerShell 5+ and an internet connection.
3+
4+
Write-Host "==> devopster setup (Windows)"
5+
6+
# ── Install Docker Desktop if missing ────────────────────────────────────────
7+
if (Get-Command docker -ErrorAction SilentlyContinue) {
8+
Write-Host "==> Docker already installed: $(docker --version)"
9+
} else {
10+
Write-Host "==> Installing Docker Desktop via winget..."
11+
if (Get-Command winget -ErrorAction SilentlyContinue) {
12+
winget install -e --id Docker.DockerDesktop
13+
} else {
14+
Write-Host "==> winget not found. Please install Docker Desktop manually:"
15+
Write-Host " https://docs.docker.com/desktop/install/windows-install/"
16+
exit 1
17+
}
18+
Write-Host ""
19+
Write-Host "==> Docker Desktop installed."
20+
Write-Host " Please start Docker Desktop, then re-run: make setup"
21+
exit 0
22+
}
23+
24+
# ── Build image and open shell ────────────────────────────────────────────────
25+
Write-Host "==> Building devopster container image..."
26+
docker build --target dev -t devopster-cli-dev .
27+
docker run --rm -it `
28+
-v "${env:USERPROFILE}\.config\devopster:/root/.config/devopster" `
29+
-v "${PWD}:/app" `
30+
-w /app `
31+
devopster-cli-dev `
32+
bash

scripts/setup.sh

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
echo "==> devopster setup"
5+
6+
# ── Install Docker if missing ─────────────────────────────────────────────────
7+
if command -v docker > /dev/null 2>&1; then
8+
echo "==> Docker already installed: $(docker --version)"
9+
else
10+
if [[ "$(uname -s)" == "Darwin" ]]; then
11+
echo "==> Installing Docker Desktop via Homebrew..."
12+
if ! command -v brew > /dev/null 2>&1; then
13+
echo "==> Installing Homebrew first..."
14+
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
15+
fi
16+
brew install --cask docker
17+
echo ""
18+
echo "==> Docker Desktop installed."
19+
echo " Please start Docker Desktop from Applications, then re-run: make setup"
20+
exit 0
21+
elif [[ -f /etc/debian_version ]]; then
22+
echo "==> Installing Docker Engine via apt..."
23+
sudo apt-get update -qq
24+
sudo apt-get install -y ca-certificates curl gnupg lsb-release
25+
sudo install -m 0755 -d /etc/apt/keyrings
26+
curl -fsSL https://download.docker.com/linux/debian/gpg \
27+
| sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
28+
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \
29+
https://download.docker.com/linux/debian $(lsb_release -cs) stable" \
30+
| sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
31+
sudo apt-get update -qq
32+
sudo apt-get install -y docker-ce docker-ce-cli containerd.io \
33+
docker-buildx-plugin docker-compose-plugin
34+
sudo usermod -aG docker "$USER"
35+
echo "==> Docker installed. You may need to log out and back in for group changes."
36+
else
37+
echo "==> Unsupported OS. Install Docker manually: https://docs.docker.com/get-docker/"
38+
exit 1
39+
fi
40+
fi
41+
42+
# ── Build image then open shell ───────────────────────────────────────────────
43+
echo "==> Building devopster container image..."
44+
make container-build
45+
46+
echo ""
47+
echo "==> Image ready. Opening container shell..."
48+
docker run --rm -it --platform linux/arm64 \
49+
-v "$HOME/.config/devopster:/root/.config/devopster" \
50+
-v "$(pwd):/app" \
51+
-w /app \
52+
devopster-cli-dev \
53+
bash

0 commit comments

Comments
 (0)