Skip to content

Commit

Permalink
Merge pull request #1 from idoavrah/dev
Browse files Browse the repository at this point in the history
version 0.1
  • Loading branch information
idoavrah authored Jul 21, 2023
2 parents 8c21bd9 + aaa52ec commit 90609d9
Show file tree
Hide file tree
Showing 13 changed files with 439 additions and 2 deletions.
40 changes: 40 additions & 0 deletions .github/workflows/publish-test-pypi.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
name: Upload Python Package (Test PyPiI)

on:
workflow_dispatch:
release:
types: [published]

permissions:
contents: read

jobs:
build:
runs-on: ubuntu-latest
steps:

- uses: actions/checkout@v3

- name: Set up Python
uses: actions/setup-python@v3
with:
python-version: '3.x'

- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install build
- name: Build package
run: python -m build

publish:
runs-on: ubuntu-latest
environment: test
permissions:
id-token: write

steps:

- name: Publish package distributions to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -158,3 +158,7 @@ cython_debug/
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/

state.txt

.terraform/
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"python.linting.enabled": false
}
26 changes: 24 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,24 @@
# terraform-tui
Terraform textual UI
# TFTUI - The terraform textual UI
`TFTUI` is a textual gui that disaplys a terraform state, and allows the user to read and interact with it.

## Features
### version 0.1.0
- [x] Display terraform state tree
- [x] Display a single resource state

## Preview



## Installation

### macOS

`gh` is available via [Homebrew][], [MacPorts][], [Conda][], [Spack][], and as a downloadable binary from the [releases page][].

#### pipx

| Install: | Upgrade: |
| ----------------- | ----------------- |
| `pipx install tftui` | `pipx upgrade tftui` |

28 changes: 28 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
[build-system]
requires = ["setuptools>=61.0.0", "wheel"]
build-backend = "setuptools.build_meta"

[project]
name = "tftui"
version = "0.1.0"
description = "Terraform Textual User Interface"
readme = "README.md"
authors = [{ name = "Ido Avraham" }]
license = { file = "LICENSE" }
classifiers = [
"License :: OSI Approved :: MIT License",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
]
keywords = ["terraform", "tui"]
dependencies = [
"textual",
"textual-dev",
]
requires-python = ">=3.9"

[project.urls]
Homepage = "https://github.com/idoavrah/terraform-tui"

[project.scripts]
tftui = "tftui.__main__:main"
63 changes: 63 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#
# This file is autogenerated by pip-compile with Python 3.11
# by the following command:
#
# pip-compile pyproject.toml
#
aiohttp==3.8.5
# via textual-dev
aiosignal==1.3.1
# via aiohttp
async-timeout==4.0.2
# via aiohttp
attrs==23.1.0
# via aiohttp
charset-normalizer==3.2.0
# via aiohttp
click==8.1.6
# via textual-dev
frozenlist==1.4.0
# via
# aiohttp
# aiosignal
idna==3.4
# via yarl
importlib-metadata==6.8.0
# via textual
linkify-it-py==2.0.2
# via markdown-it-py
markdown-it-py[linkify,plugins]==3.0.0
# via
# mdit-py-plugins
# rich
# textual
mdit-py-plugins==0.4.0
# via markdown-it-py
mdurl==0.1.2
# via markdown-it-py
msgpack==1.0.5
# via textual-dev
multidict==6.0.4
# via
# aiohttp
# yarl
pygments==2.15.1
# via rich
rich==13.4.2
# via textual
textual==0.30.0
# via
# textual-dev
# tftui (pyproject.toml)
textual-dev==1.0.1
# via tftui (pyproject.toml)
typing-extensions==4.7.1
# via
# textual
# textual-dev
uc-micro-py==1.0.2
# via linkify-it-py
yarl==1.9.2
# via aiohttp
zipp==3.16.2
# via importlib-metadata
Empty file added src/tftui/__init__.py
Empty file.
145 changes: 145 additions & 0 deletions src/tftui/__main__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
from textual.app import App, ComposeResult, Binding
from textual.widgets import Header, Footer, Tree, TextLog, LoadingIndicator, ContentSwitcher, Static
import subprocess, time

class TerraformTUI(App):

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.state = {}
self.currentNode = None

TITLE = "Terraform TUI"
SUB_TITLE = "The textual UI for Terraform"
CSS_PATH = "ui.css"

BINDINGS = [
("Enter", "", "View state"),
Binding("escape", "back", "Back"),
# ("d", "destroy", "Destroy"),
# ("t", "taint", "Taint"),
# ("u", "untaint", "Untaint"),
# ("r", "refresh", "Refresh"),
("m", "toggle_dark", "Toggle dark mode"),
("q", "quit", "Quit")
]

def compose(self) -> ComposeResult:
yield Header(classes="header")
with ContentSwitcher(id="switcher", initial="loading"):
yield LoadingIndicator(id="loading")
yield Tree("State", id="tree", classes="tree")
yield TextLog(id="pretty", highlight=True, markup=True, wrap=True, classes="pretty", auto_scroll=False)
yield Static(id="status", classes="status")
yield Footer()

def on_mount(self) -> None:
tree = self.get_widget_by_id("tree")
tree.guide_depth = 3
data = ""

status = self.get_widget_by_id("status")
status.update("Loading state...")
result = subprocess.run(["terraform", "show", "-no-color"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)

if result.returncode != 0:
status.update(f"{result.stderr} {result.stdout}")
return
elif not result.stdout.startswith('#'):
status.update(f"{result.stderr} {result.stdout}")
return
status.update("")

for line in result.stdout.splitlines():

# regular resource
if line.startswith('#') and not line.startswith('# module'):
leaf = tree.root.add_leaf(line[2:-1])

# module
elif line.startswith('# module'):
node = tree.root
items = []
leaf = ""
parts = line[2:-1].split('.')
for part in parts:
if part == "module":
isModule = True
continue
elif isModule:
isModule = False
items.append(f"module.{part}")
continue
leaf = f"{leaf}.{part}"
items.append(leaf[1:])

key = ""
for item in items:
isModule = False
key = f"{key}.{item}"
if item.startswith("module"):
isModule = True
if self.state.get(key) is None:
if isModule:
node = node.add(item)
else:
leaf = node.add_leaf(item)
self.state[key] = node
else:
node = self.state[key]

# data portion of resource
else:
data += line + "\n"

if len(line) == 1:
leaf.data = data.strip()
data = ""

tree.root.expand_all()
self.get_widget_by_id("switcher").current = "tree"

def on_tree_node_highlighted(self, node) -> None:
self.currentNode = node.node
pretty = self.get_widget_by_id("pretty")
if self.currentNode.data is not None:
pretty = self.get_widget_by_id("pretty")
pretty.clear()
pretty.write(self.currentNode.data)
else:
self.currentNode = None

def on_tree_node_selected(self) -> None:
if (self.currentNode is None):
return
self.get_widget_by_id("switcher").current = "pretty"

def action_back(self) -> None:
switcher = self.get_widget_by_id("switcher")
if not switcher.current == "pretty":
return
switcher.current = "tree"

def action_destroy(self) -> None:
if not self.get_widget_by_id("switcher").current == "tree":
return

def action_taint(self) -> None:
if not self.get_widget_by_id("switcher").current == "tree":
return

def action_untaint(self) -> None:
if not self.get_widget_by_id("switcher").current == "tree":
return

def action_refresh(self) -> None:
if not self.get_widget_by_id("switcher").current == "tree":
return

def action_toggle_dark(self) -> None:
self.dark = not self.dark

def main() -> None:
app = TerraformTUI()
app.run()

22 changes: 22 additions & 0 deletions src/tftui/ui.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
.header {
height: 3;
background: blue;
color: white;
}

.tree {
margin: 1;
border: blue;
}

.status {
height: 3;
margin: 0;
color: red;
text-align: center;
}

.pretty {
border: blue;
margin: 1;
}
22 changes: 22 additions & 0 deletions test/.terraform.lock.hcl

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

21 changes: 21 additions & 0 deletions test/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
terraform {
required_providers {
random = {
source = "hashicorp/random"
version = "3.5.1"
}
}
}

provider "random" {
}

module "random_module" {
source = "./module"
input_number = random_integer.random_number.result
}

resource "random_integer" "random_number" {
min = 1
max = 100
}
Loading

0 comments on commit 90609d9

Please sign in to comment.