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

Release workflow #10

Merged
merged 9 commits into from
Jun 21, 2024
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions .github/workflows/index.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
name: Index Latest Release

on:
workflow_dispatch:

jobs:
index:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2

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

- name: Install Dependencies
run: |
python -m pip install --upgrade pip
pip install aiohttp
pip install aiofiles
pip install requests
pip install elasticsearch==7.13.4
sudo apt-get install p7zip-full

- name: Run Index Script
env:
ES_HOST: ${{ secrets.ES_HOST }}
ES_INDEX: ${{ secrets.ES_INDEX }}
ES_USER: ${{ secrets.ES_USER }}
ES_PASSWORD: ${{ secrets.ES_PASSWORD }}
run: python -u scripts/index.py ${{ github.repository }} ${{ secrets.GITHUB_TOKEN }}
26 changes: 26 additions & 0 deletions .github/workflows/release.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
name: Release Asset Upload

on:
release:
types: [published]

jobs:
upload-release-asset:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2

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

- name: Install Dependencies
run: |
python -m pip install --upgrade pip
pip install requests
pip install py7zr

- name: Run Release Script
run: python -u scripts/package.py ${{ secrets.GITHUB_TOKEN }} ${{ github.repository }}
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
*.asm
.vscode
output
__pycache__
*.7z
tmp
132 changes: 132 additions & 0 deletions scripts/index.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
import os, time, argparse, requests
from elasticsearch import Elasticsearch

import support as support

# Gets latest release headers from repository
def get_headers(api, token):
if api:
return {
'Authorization': f'token {token}'
}
else:
return {
'Authorization': f'Bearer {token}',
'Accept': 'application/octet-stream'
}

# Function to fetch release details from GitHub
def fetch_release_details(repo, token):
api_headers = get_headers(True, token)
url = f'https://api.github.com/repos/{repo}/releases'
response = requests.get(url, headers=api_headers)
response.raise_for_status() # Raise an exception for HTTP errors
return support.get_latest_release(response.json())

# Function to fetch content as JSON from the link
def fetch_json_data(download_link, token):
"""
Fetches JSON data from the specified URL using an authorization token and returns it as a dictionary.

Args:
download_link (str): URL from which to fetch the JSON data.

Returns:
tuple: The first element is a dictionary containing the JSON data (or None in case of failure),
the second element is an error message or None if no errors occurred.
"""
headers = get_headers(False, token)

try:
response = requests.get(download_link, headers=headers)
response.raise_for_status()
return response.json(), None
except requests.RequestException as e:
print(f"Error fetching JSON data: {e}")
return None, str(e)

# Function to find an item by name
def find_item_by_name(items, name):
for item in items:
if item['name'] == name:
return item
return None

# Function to index release details into Elasticsearch
def index_release_to_elasticsearch(es : Elasticsearch, index_name, release_details, token):
version = None
for asset in release_details.get('assets', []):
if 'mikrosdk.7z' == asset['name']:
# Download the current mikroSDK asset in order to read the version
support.extract_archive_from_url(
asset['url'],
os.path.join(os.path.dirname(__file__), 'tmp'),
token
)

# Then fetch version from manifest file
version = support.fetch_version_from_asset(os.path.join(os.path.dirname(__file__), 'tmp'))
break
for asset in release_details.get('assets', []):
doc = None
name_without_extension = os.path.splitext(os.path.basename(asset['name']))[0]
if 'mikrosdk' == name_without_extension:
doc = {
'name': name_without_extension,
'display_name': "mikroSDK",
'author': 'MIKROE',
'hidden': False,
'type': 'sdk',
'version': version,
'created_at' : asset['created_at'],
'updated_at' : asset['updated_at'],
'category': 'Software Development Kit',
'download_link': asset['url'], # Adjust as needed for actual URL
'package_changed': True
}
elif 'templates' == name_without_extension:
doc = {
"name": name_without_extension,
"version" : version,
"display_name" : "NECTO project templates",
"hidden" : True,
"vendor" : "MIKROE",
"type" : "application",
"location" : f"necto/%os_type%/{asset['name']}.7z",
StrahinjaJacimovic marked this conversation as resolved.
Show resolved Hide resolved
"install_location" : "%APPLICATION_DATA_DIR%",
StrahinjaJacimovic marked this conversation as resolved.
Show resolved Hide resolved
"dependencies": []
StrahinjaJacimovic marked this conversation as resolved.
Show resolved Hide resolved
}

# Index the document
if doc:
resp = es.index(index=index_name, doc_type='necto_package', id=name_without_extension, body=doc)
print(f"{resp["result"]} {resp['_id']}")

if __name__ == '__main__':
# Get arguments
parser = argparse.ArgumentParser(description="Upload directories as release assets.")
parser.add_argument("repo", help="Repository name, e.g., 'username/repo'")
parser.add_argument("token", help="GitHub Token")
args = parser.parse_args()

# Elasticsearch instance used for indexing
num_of_retries = 1
while True:
print(f"Trying to connect to ES. Connection retry: {num_of_retries}")
es = Elasticsearch([os.environ['ES_HOST']], http_auth=(os.environ['ES_USER'], os.environ['ES_PASSWORD']))
if es.ping():
break
# Wait for 30 seconds and try again if connection fails
if 10 == num_of_retries:
# Exit if it fails 10 times, something is wrong with the server
raise ValueError("Connection to ES failed!")
num_of_retries += 1

time.sleep(30)

# Now index the new release
index_release_to_elasticsearch(
es, os.environ['ES_INDEX'],
fetch_release_details(args.repo, args.token),
args.token
)
84 changes: 84 additions & 0 deletions scripts/package.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import os, re, py7zr, requests, argparse, json

def find_manifest_folder(base_dir):
"""Find the folder containing 'manifest.json'."""
for root, dirs, files in os.walk(base_dir):
if 'manifest.json' in files:
return root
return None

def create_7z_archive(version, source_folder, archive_path):
"""Create a .7z archive from a source folder with a specific folder structure, excluding the .github folder."""
with py7zr.SevenZipFile(archive_path, 'w') as archive:
for root, dirs, files in os.walk(source_folder):
if re.search(r'(\.git)|(scripts)|(templates)', os.path.relpath(root, source_folder)):
continue
for file in files:
file_path = os.path.join(root, file)
# Exclude the archive itself
if file_path == archive_path:
continue
archive.write(file_path, os.path.join(version, 'src', os.path.relpath(file_path, source_folder)))

def create_template_archive(source_folder, archive_path):
"""Create a .7z archive from a source folder with a specific folder structure."""
with py7zr.SevenZipFile(archive_path, 'w') as archive:
os.chdir(source_folder)
archive.writeall('./')
os.chdir('..')

def upload_asset_to_release(repo, release_id, asset_path, token):
"""Upload an asset to a specific GitHub release."""
url = f'https://uploads.github.com/repos/{repo}/releases/{release_id}/assets?name={os.path.basename(asset_path)}'
headers = {
'Authorization': f'token {token}',
'Content-Type': 'application/x-7z-compressed'
}
with open(asset_path, 'rb') as file:
response = requests.post(url, headers=headers, data=file)
response.raise_for_status()
print(f'Uploaded asset: {os.path.basename(asset_path)} to release ID: {release_id}')
return response.json()

def get_release_id(repo, tag_name, token):
"""Get the release ID for a given tag name."""
url = f'https://api.github.com/repos/{repo}/releases/tags/{tag_name}'
headers = {'Authorization': f'token {token}'}
response = requests.get(url, headers=headers)
response.raise_for_status()
return response.json()['id']

if __name__ == '__main__':
parser = argparse.ArgumentParser(description="Upload directories as release assets.")
parser.add_argument("token", help="GitHub Token")
parser.add_argument("repo", help="Repository name, e.g., 'username/repo'")
args = parser.parse_args()

# Assuming the repository is checked out at the root directory
repo_dir = os.getcwd()
manifest_folder = find_manifest_folder(repo_dir)
version = json.load(open(os.path.join(manifest_folder ,'manifest.json')))['sdk-version']

if manifest_folder:
archive_path = os.path.join(repo_dir, 'mikrosdk.7z')
print('Creating archive: %s' % archive_path)
create_7z_archive(version, repo_dir, archive_path)
print('Archive created successfully: %s' % archive_path)

# Get the release ID and upload the asset
release_id = get_release_id(args.repo, f'mikroSDK-{version}', args.token)
upload_result = upload_asset_to_release(args.repo, release_id, archive_path, args.token)

print('Asset "%s" uploaded successfully to release ID: %s' % ('mikrosdk', release_id))

if os.path.exists(os.path.join(repo_dir, 'templates')):
archive_path = os.path.join(repo_dir, 'templates.7z')
print('Creating archive: %s' % archive_path)
create_template_archive('templates', archive_path)
print('Archive created successfully: %s' % archive_path)

# Get the release ID and upload the asset
release_id = get_release_id(args.repo, f'mikroSDK-{version}', args.token)
upload_result = upload_asset_to_release(args.repo, release_id, archive_path, args.token)

print('Asset "%s" uploaded successfully to release ID: %s' % ('templates', release_id))
79 changes: 79 additions & 0 deletions scripts/support.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import requests, py7zr, zipfile, io, os, json

def get_previous_release(releases, prerelases=None):
''' Fetch the previously released version '''
for counter, release in enumerate(releases):
if not release['draft']:
if prerelases:
if release['prerelease']:
continue
if counter + 1 < len(releases):
return releases[counter + 1]
else:
return None
return None

def get_latest_release(releases):
''' Fetch the latest released version '''
return next((release for release in releases if not release['prerelease'] and not release['draft']), None)

def determine_archive_type(byte_stream):
'''
Implement logic to determine the archive type, e.g., by file extension or magic number
For simplicity, let's assume byte_stream has a 'name' attribute (e.g., a file-like object)
'''
byte_stream.seek(0)
signature = byte_stream.read(4)
byte_stream.seek(0)
if signature == b'PK\x03\x04': # ZIP magic number, it is what it is
return 'zip'
else:
return '7z'

def extract_archive_from_url(url, destination, token):
"""
Extract the contents of an archive (7z or zip) from a URL directly
in memory, without downloading the file.
"""
print(f"Download link: {url}")
headers = {
'Authorization': f'token {token}',
'Accept': 'application/octet-stream'
}
if 'github' in url:
response = requests.get(url, headers=headers, stream=True)
else:
response = requests.get(url, stream=True)

response.raise_for_status()

if response.status_code == 200: ## Response OK?
with io.BytesIO() as byte_stream:

for chunk in response.iter_content(chunk_size=8192):
byte_stream.write(chunk)

byte_stream.seek(0)

archive_type = determine_archive_type(byte_stream)

if archive_type == '7z':
with py7zr.SevenZipFile(byte_stream, mode='r') as archive:
archive.extractall(path=destination)
elif archive_type == 'zip':
with zipfile.ZipFile(byte_stream, mode='r') as archive:
for info in archive.infolist():
archive.extract(info, path=destination)
else:
raise ValueError("Unsupported archive type")
else:
raise Exception(f"Failed to download file: status code {response.status_code}")

def fetch_version_from_asset(asset_path):
for root, subdirs, files in os.walk(asset_path):
for file_name in files:
if 'manifest.json' == file_name:
return json.load(open(os.path.join(root, file_name)))['sdk-version']
else:
continue
return None
21 changes: 21 additions & 0 deletions templates/designer_generated_code/codegen_screen.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*This is a comment.*/

#include "vtft.h"
#include <string.h>
#include <assembly.h>
#include "resource.h"
#include "scr_%1.h"

%2

void initialize_%1()
{
%3
}

int get_%1_initialized()
{
return %1_initialized;
}


Loading
Loading