Skip to content
This repository has been archived by the owner on Jun 2, 2024. It is now read-only.

Commit

Permalink
ci: build in PRs (#202)
Browse files Browse the repository at this point in the history
* ci: build in PRs

* ci: improve naming

* ci: upload individual artifacts

* ci: try running when PRs are pushed to

* ci: name

* ci: try individual again

* ci: friendship ended with gha; gitlab ci/cd is my new friend

* feat: introduce --from-artifact opt in install script
  • Loading branch information
nullishamy committed May 20, 2024
1 parent 6eeda71 commit 44be6bb
Show file tree
Hide file tree
Showing 4 changed files with 146 additions and 26 deletions.
33 changes: 33 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
name: "Generate test artifacts"

on:
pull_request:
types: [opened, reopened, synchronize]
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
submodules: true
- uses: actions/setup-python@v5
with:
python-version: "3.11"
cache: "pip"
- name: Install dependencies
run: pip install -r requirements.txt
- name: Install colloid specific dependencies
run: sudo apt update && sudo apt install -y sassc inkscape optipng
- name: Generate themes
run: |
python ./build.py mocha --all-accents --zip -d $PWD/releases &&
python ./build.py macchiato --all-accents --zip -d $PWD/releases &&
python ./build.py frappe --all-accents --zip -d $PWD/releases &&
python ./build.py latte --all-accents --zip -d $PWD/releases
- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: '${{ github.sha }}-artifacts'
path: ./releases/*.zip

2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: "Release"
name: "Mainline release"

on:
push:
Expand Down
22 changes: 21 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,24 @@ Once the build script patches the submodule, it will write a file into
The palette patches are generated through `whiskers`,
so if you're changing them, they will need regenerated. Simply run `whiskers palette.tera` to rebuild them.

The process for building the theme is [documented in the README](./README.md#building).
The process for building the theme is [documented in the README](./README.md#building).

### Running test builds
We support building and publishing test builds from PRs. When you open PRs, the CI will automatically build with your changes and push an artifact
which bundles all of the produced themes.

You can then download the artifacts as a zip (result should look similar to 7bff2448a81e36bf3b0e03bfbd649bebe6973ec7-artifacts.zip) and
pass the path into `install.py` under the `--from-artifact` option:
```bash
python3 install.py mocha blue --dest ./build --from-artifact ~/downloads/7bff2448a81e36bf3b0e03bfbd649bebe6973ec7-artifacts.zip
```

This will take the target flavor / accent out of the zip, and install it using the regular install process.

It is advised to pass a `--dest` when running in this mode, because the released zips follow the exact same naming scheme as regular builds.
This wil cause conflicts when you install, if you already had that theme installed. Passing a different destination allows you to move the
extracted folders to `~/.local/share/themes` yourself, adding a suffix as appropriate to avoid conflicts.

> [!WARNING]
> If you pass `--link` to the install script when working from a PR, it will forcibly overwrite your `~/.config/gtk-4.0/` symlinks.
> You will have to reinstall / relink to revert this.
115 changes: 91 additions & 24 deletions install.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
#!/usr/bin/env python3

import os, zipfile, argparse, logging, io
from typing import Optional
from pathlib import Path
from dataclasses import dataclass
from urllib.request import urlopen, Request
from urllib.parse import urlparse

logger = logging.getLogger("catppuccin-gtk")
logger.setLevel(logging.DEBUG)
Expand All @@ -20,6 +22,17 @@ class InstallContext:
dest: Path
link: bool

def build_info(self, include_url=True) -> str:
url = build_release_url(self)
info = f"""Installation info:
flavor: {self.flavor}
accent: {self.accent}
dest: {self.dest.absolute()}
link: {self.link}"""
if include_url:
info += f"\nremote_url: {url}"
return info


def parse_args():
parser = argparse.ArgumentParser()
Expand Down Expand Up @@ -53,6 +66,13 @@ def parse_args():
help="Accent of the theme.",
)

parser.add_argument(
"--from-artifact",
type=Path,
dest="from_artifact",
help="Install from an artifact instead of a mainline release, pass the artifact path (e.g 7bff2448a81e36bf3b0e03bfbd649bebe6973ec7-artifacts.zip)",
)

parser.add_argument(
"--dest",
"-d",
Expand Down Expand Up @@ -80,21 +100,12 @@ def build_release_url(ctx: InstallContext) -> str:
return f"{repo_root}/{release}/{zip_name}"


def install(ctx: InstallContext):
url = build_release_url(ctx)
build_info = f"""Installation info:
flavor: {ctx.flavor}
accent: {ctx.accent}
dest: {ctx.dest.absolute()}
link: {ctx.link}
remote_url: {url}"""
logger.info(build_info)
httprequest = Request(url)
def fetch_zip(url: str) -> Optional[zipfile.ZipFile]:
req = Request(url)

zip_file = None
logger.info("Starting download...")
with urlopen(httprequest) as response:
with urlopen(req) as response:
logger.info(f"Response status: {response.status}")
zip_file = zipfile.ZipFile(io.BytesIO(response.read()))
logger.info("Download finished, zip is valid")
Expand All @@ -103,28 +114,81 @@ def install(ctx: InstallContext):
first_bad_file = zip_file.testzip()
if first_bad_file is not None:
logger.error(f'Zip appears to be corrupt, first bad file is "{first_bad_file}"')
return
return None
logger.info("Download verified")
return zip_file


def add_libadwaita_links(ctx: InstallContext, rewrite: True):
dir_name = (
ctx.dest / f"catppuccin-{ctx.flavor}-{ctx.accent}-standard+default" / "gtk-4.0"
).absolute()
gtk4_dir = (Path(os.path.expanduser("~")) / ".config" / "gtk-4.0").absolute()
os.makedirs(gtk4_dir, exist_ok=True)

logger.info("Adding symlinks for libadwaita")
logger.info(f"Root: {dir_name}")
logger.info(f"Target: {gtk4_dir}")
try:
if rewrite:
os.remove(dir_name / "assets", gtk4_dir / "assets")
os.remove(dir_name / "gtk.css", gtk4_dir / "gtk.css")
os.remove(dir_name / "gtk-dark.css", gtk4_dir / "gtk-dark.css")
except FileNotFoundError:
logger.debug("Ignoring FileNotFound in symlink rewrite")

os.symlink(dir_name / "assets", gtk4_dir / "assets")
os.symlink(dir_name / "gtk.css", gtk4_dir / "gtk.css")
os.symlink(dir_name / "gtk-dark.css", gtk4_dir / "gtk-dark.css")


def install(ctx: InstallContext):
url = build_release_url(ctx)
logger.info(ctx.build_info())

zip_file = fetch_zip(url)
if zip_file is None:
return

logger.info("Extracting...")
zip_file.extractall(ctx.dest)
logger.info("Extraction complete")

if ctx.link:
dir_name = (ctx.dest / f"catppuccin-{ctx.flavor}-{ctx.accent}-standard+default" / 'gtk-4.0').absolute()
gtk4_dir = (Path(os.path.expanduser('~')) / '.config' / 'gtk-4.0').absolute()
os.makedirs(gtk4_dir, exist_ok=True)
add_libadwaita_links(ctx)

def install_from_artifact(ctx: InstallContext, artifact_path: Path):
# Working from a pull request, special case it
logger.info(f"Extracting artifact from '{artifact_path}'")
artifacts = zipfile.ZipFile(artifact_path)

logger.info("Verifying artifact...")
first_bad_file = artifacts.testzip()
if first_bad_file is not None:
logger.error(f'Zip appears to be corrupt, first bad file is "{first_bad_file}"')
return None
logger.info("Artifact verified")

logger.info("Adding symlinks for libadwaita")
logger.info(f'Root: {dir_name}')
logger.info(f'Target: {gtk4_dir}')
os.symlink(dir_name / 'assets', gtk4_dir / 'assets')
os.symlink(dir_name / 'gtk.css', gtk4_dir / 'gtk.css')
os.symlink(dir_name / 'gtk-dark.css', gtk4_dir / 'gtk-dark.css')
logger.info(ctx.build_info(False))

# The zip, inside the artifacts, that we want to pull out
zip_name = f"catppuccin-{ctx.flavor}-{ctx.accent}-standard+default.zip"
logger.info(f"Pulling '{zip_name}' from the artifacts")
info = artifacts.getinfo(zip_name)

logger.info("Extracting the artifact...")
artifact = zipfile.ZipFile(io.BytesIO(artifacts.read(info)))
artifact.extractall(ctx.dest)
logger.info("Extraction complete")

if ctx.link:
logger.info("Adding links (with rewrite)")
add_libadwaita_links(ctx, True)
logger.info("Links added")

def main():
args = parse_args()

dest = Path(os.path.expanduser("~")) / ".local" / "share" / "themes"
os.makedirs(dest, exist_ok=True)

Expand All @@ -135,9 +199,12 @@ def main():
flavor=args.flavor, accent=args.accent, dest=dest, link=args.link
)

install(ctx)
if args.from_artifact:
install_from_artifact(ctx, args.from_artifact)
return

logger.info('Theme installation complete!')
install(ctx)
logger.info("Theme installation complete!")


try:
Expand Down

0 comments on commit 44be6bb

Please sign in to comment.