Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add workflow to generate file containing release information for applets #4314

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
37 changes: 37 additions & 0 deletions .github/workflows/releases.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# This is a basic workflow to help you get started with Actions

name: Update Releases

# Controls when the action will run.
on:
# Triggers the workflow on push events but only for the master branch
push:
branches: [ master ]

# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:

# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
# This workflow contains a single job called "build"
update-tables:
# The type of runner that the job will run on
runs-on: ubuntu-20.04

# Steps represent a sequence of tasks that will be executed as part of the job
steps:
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
- uses: actions/checkout@v2

# Runs a set of commands using the runners shell
- name: Generate Translation Status Tables
run: |
sudo apt-get update -y >/dev/null 2>&1
sudo apt-get install -y python3 gettext >/dev/null 2>&1
cd .applet-versions
python3 release_generator.py
git config user.name "NikoKrause"
git config user.email "[email protected]"
Comment on lines +33 to +34
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this have one user's credentials hard-coded?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's been a long time, but I think I copied it from the workflow to generate the translation tables.

I'm not sure what I should be using, if it were on me I would use a generic CI account

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm going to take a shot at adding some workflows in a few weeks that will work on a similar basis. Let's see what we can come up with that is a cleaner design.

git add .
git commit --quiet -m "Update releases (GitHub Actions)"
git push -f origin master:releases
32 changes: 32 additions & 0 deletions .releases/release_generator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
from typing import Dict
from utils.version_generator import generate_applet_version_map, Applet
import os
from pathlib import Path
import json
from itertools import zip_longest

REPO_FOLDER = os.path.realpath(os.path.abspath(os.path.join(
os.path.normpath(os.path.join(os.getcwd(), *([".."] * 1))))))

releasesPath = Path("releases.json")
if __name__ == "__main__":
version_map = generate_applet_version_map()

old_version_map: Dict[str, Applet] = {}
if (releasesPath.exists()):
with open(releasesPath, "r") as f:
old_version_map = json.load(f)

# compare
for [id, applet] in version_map.items():
old_applet = old_version_map.get(applet["id"], None)
old_applet_versions = old_applet["versions"] if old_applet is not None else []

for new, old in zip_longest(applet["versions"], old_applet_versions, fillvalue=None):
# Should not care about the order of the versions, they should be sorted already
if old is None:
# New version compared to the old file
pass

with open(releasesPath, "w") as f:
json.dump(version_map, f, indent=4)
Empty file added .releases/utils/__init__.py
Empty file.
135 changes: 135 additions & 0 deletions .releases/utils/version_generator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
import os
from pathlib import Path
from typing import Dict, List, Optional, Tuple, TypedDict
import subprocess
import json
from multiprocessing import Pool

class Version(TypedDict):
version: str
commit: str
date: str
url: str
applet_checksum: str
message: str

class Metadata(TypedDict):
uuid: str
name: str
description: str
version: Optional[str]

class Applet(TypedDict):
id: str
path: str
versions: List[Version]

REPO_FOLDER = Path(subprocess.check_output([
"git",
"rev-parse",
"--show-toplevel"
], encoding="utf-8").strip())

def get_current_metadata(path: Path) -> Metadata:
with open(path, "r") as f:
return json.load(f)

def get_applet_hash(applet_folder: Path, hash: str) -> Optional[str]:
'''Gets hash of the applet "dist" ({name}/files/{name}) folder'''
try:
output = subprocess.check_output([
"git",
"rev-parse",
f"{hash}:{str(applet_folder)}",

], cwd=REPO_FOLDER, encoding="utf-8")
except subprocess.CalledProcessError as e:
return None
return output.strip()

def obtain_versions(applet: Applet) -> List[Version]:
versions: List[Version] = []
metadataPath = Path(applet['path']).joinpath(f'metadata.json')

# Get changelog for applet
output = subprocess.check_output([
"git",
"log",
"--pretty=format:%h;%aI;%s%d",
"--",
str(applet['path'])
], cwd=REPO_FOLDER, encoding="utf-8")

previousVersion: Optional[str] = None
currentRevision: int = 1
for line in reversed(output.splitlines()):
if line is None:
continue

# Get information on commit
[commit, timestamp, message] = line.split(";", 2)
hash = get_applet_hash(Path(applet["path"]), commit)
if (hash is None):
# No files in applet dist folder, skip
continue

try:
metadata: Metadata = json.loads(subprocess.check_output([
"git",
"show",
f"{commit}:{str(metadataPath)}"
], cwd=REPO_FOLDER))
except subprocess.CalledProcessError:
# No metadata.json file in commit, skip
continue

# Normalize version, can't be null and we add revision that we increment when version was not changed
version = str(metadata["version"]) if metadata.get("version") is not None else "0.0.0"
if (version == previousVersion):
currentRevision += 1
else:
currentRevision = 1

versions.append({
"version": f"{version}-{str(currentRevision)}",
"commit": commit,
"message": message,
"date": timestamp,
"applet_checksum": hash,
"url": f"https://github.com/linuxmint/cinnamon-spices-applets/archive/{commit}.zip"
})
previousVersion = version

return versions

def get_applet_info(metadata: Metadata, item: Path) -> Applet:
applet: Applet = {
"id": metadata["uuid"],
"path": str(item.joinpath("files/" + item.name).relative_to(REPO_FOLDER)),
"versions": []
}

applet["versions"] = obtain_versions(applet)
return applet

def generate_applet_version_map() -> Dict[str, Applet]:
applets: Dict[str, Applet] = {}
pool = Pool()
items: List[Tuple[Metadata, Path]] = []
for item in sorted(REPO_FOLDER.iterdir()):
if not item.is_dir():
continue
metadataFile = item.joinpath(f"files/{item.name}/metadata.json")
if (not metadataFile.exists() and not metadataFile.is_file()):
continue

metadata = get_current_metadata(metadataFile)
items.append((metadata, item))

results = pool.starmap(get_applet_info, items)
for result in results:
applets[result["id"]] = result
return applets

if __name__ == "__main__":
print(json.dumps(generate_applet_version_map(), indent=4))