Skip to content

New release flow #13

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

Merged
merged 5 commits into from
Jun 3, 2025
Merged
Show file tree
Hide file tree
Changes from all 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
2 changes: 1 addition & 1 deletion .github/workflows/package-core.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ jobs:
- name: Set packager options
run: |
if [ "${{ inputs.release }}" = "false" ]; then
echo "packager_opts=--no-version-check --no-previous-releases" >> $GITHUB_ENV
echo "packager_opts=--no-previous-releases" >> $GITHUB_ENV
else
echo "packager_opts=" >> $GITHUB_ENV
fi
Expand Down
113 changes: 75 additions & 38 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,53 +1,90 @@
name: Release

on:
release:
types: published
push:
tags:
- '[0-9]+.[0-9]+.[0-9]+**'
workflow_call:
inputs:
release-title:
required: true
type: string
description: 'The title of the release'
setup-script:
required: false
type: string
default: null
description: 'Any setup script to run before building the release assets'
version:
required: false
default: ''
type: string
description: 'Version to be used for the release. It can be a semver version or a bump type (major, minor, patch).'

jobs:

core-package:
if: startsWith(github.ref, 'refs/tags/')
uses: Infineon/arduino-devops/.github/workflows/package-core.yml@latest
with:
setup-script: ${{ inputs.setup-script }}
release: true
secrets: inherit

release:
needs: core-package
runs-on: ubuntu-latest

if: startsWith(github.ref, 'refs/tags/')
steps:
- name: Retrieve core package
uses: actions/download-artifact@v4

- name: Build release changelog
id: build_changelog
uses: mikepenz/release-changelog-builder-action@v3
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Upload assets
uses: softprops/action-gh-release@v1
with:
name: ${{ inputs.release-title }} ${{ github.ref_name }}
files: |
arduino-core-package/*.zip
arduino-core-package/*.json
body: ${{steps.build_changelog.outputs.changelog}}
- name: Checkout repository
uses: actions/checkout@v4
with:
submodules: true
fetch-tags: true
fetch-depth: 0

- name: Checkout arduino-devops
uses: actions/checkout@v4
with:
repository: Infineon/arduino-devops
ref: latest
path: arduino-devops

- name: Install python dependencies
run: |
python -m pip install --upgrade pip
pip install -r arduino-devops/requirements.txt

- name: Auto tag release
if: ${{ inputs.version != '' }}
run: |
python ./arduino-devops/arduino-release.py new ${{ inputs.version }}

- name: Release verify
run: |
python ./arduino-devops/arduino-release.py verify

- name: Release info
id: release_info
run: |
head_tag=$(python ./arduino-devops/arduino-release.py info head-tag)
repo_name=$(python ./arduino-devops/arduino-release.py info repo-name)
asset_type=$(python ./arduino-devops/arduino-release.py info asset-type)

echo "Repository name: $repo_name"
echo "Asset type: $asset_type"
echo "Head tag: $head_tag"

echo "tag="$head_tag"" >> $GITHUB_OUTPUT
echo "repo_name="$repo_name"" >> $GITHUB_OUTPUT
echo "asset_type="$asset_type"" >> $GITHUB_OUTPUT

- name: Release setup
if : ${{ inputs.setup-script }} != null
Copy link
Preview

Copilot AI May 30, 2025

Choose a reason for hiding this comment

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

[nitpick] Remove the extra space between 'if' and ':' to ensure proper YAML syntax; use 'if:' instead of 'if :'.

Suggested change
if : ${{ inputs.setup-script }} != null
if: ${{ inputs.setup-script }} != null

Copilot uses AI. Check for mistakes.

run: |
${{ inputs.setup-script }}

- name: Pack arduino core
if: ${{ steps.release_info.outputs.asset_type == 'core' }}
run: |
python ./arduino-devops/arduino-packager.py

- name: Build release changelog
id: build_changelog
uses: mikepenz/release-changelog-builder-action@v5
with:
mode: "HYBRID"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Release
uses: softprops/action-gh-release@v2
with:
name: ${{ steps.release_info.outputs.repo_name }} ${{ steps.release_info.outputs.tag }}
tag_name: ${{ steps.release_info.outputs.tag }}
files: |
build/*.zip
build/*.json
body: ${{steps.build_changelog.outputs.changelog}}
144 changes: 92 additions & 52 deletions arduino-packager.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import os
import re
import requests
import semver
import shutil
import sys
import subprocess
Expand Down Expand Up @@ -164,32 +165,6 @@ def __init__(self):
self.tag = self.__strip_prefix_from_version(git_tag)
self.commit = git_sha

def check_consistency(self, platform_txt):
"""
The version consistency check is done by comparing the git
versioning and the one in platform.txt.

These need to be matching in a definitive release.

Args:
- platform_txt: The path to the platform.txt file.
"""

# Get the version from the platform.txt
with open(platform_txt, "r") as f:
lines = f.readlines()
for line in lines:
if "version=" in line:
platform_version = line.split("=")[1].strip()
break

# Check if the git tag is the same as the platform version
if self.tag != platform_version:
logging.error(
f"Git tag version {self.tag} does not match the platform version {platform_version}"
)
sys.exit(1)

""" Private methods """

def __strip_prefix_from_version(self, version):
Expand Down Expand Up @@ -338,20 +313,50 @@ def __copy_includes(self, include, exclude):

def __add_version(self, package_version):
"""
Creates a file with the version information and
add it to the package.
Adds the version information to the package in
the platform.txt metadata file and as a separate file
including the tag and commit sha.

Args:
- package_version: The package version object.
"""
version_file = os.path.join(self.pkg_path, ".version")
with open(version_file, "w") as f:
f.write(f"tag: {package_version.tag}\n")
f.write(f"commit: {package_version.commit }\n")
def add_version_to_platform_txt():
"""
Adds the version information to the platform.txt file.

The platform.txt contains a line with the variable "version="
Anything after the "=" will be replaced with the version information.

"""
platform_txt = os.path.join(self.pkg_path, "platform.txt")

logging.info(f"Added version information to package in {version_file}")
logging.info(f" - tag: {package_version.tag}")
logging.info(f" - commit: {package_version.commit}")
with open(platform_txt, "r") as f:
lines = f.readlines()

with open(platform_txt, "w") as f:
for line in lines:
if "version=" in line:
f.write(f"version={package_version.tag}\n")
else:
f.write(line)
logging.info(f"Added version information to platform.txt in {platform_txt}")

def add_version_file():
"""
Creates a file with the version information and
add it to the package.
"""
version_file = os.path.join(self.pkg_path, ".version")
with open(version_file, "w") as f:
f.write(f"tag: {package_version.tag}\n")
f.write(f"commit: {package_version.commit }\n")

logging.info(f"Added version information to package in {version_file}")
logging.info(f" - tag: {package_version.tag}")
logging.info(f" - commit: {package_version.commit}")

add_version_to_platform_txt()
add_version_file()

def __zip(self):
"""
Expand Down Expand Up @@ -467,12 +472,60 @@ def __set_package_download_url(self):
def __get_latest_release_package_index(self):
"""
Gets the latest release package index from the server.
The latest is considered the previous release version.
This avoid conflicts when triggering the release directly
from the github release section.

Supported servers:
- Github

Returns an empty index if the package index file does not exist.
"""
def get_previous_release_tag():
"""
Get the previous release tag for the repository from the github server.
"""
def get_repo_releases():
"""
Get the list of releases from a the github repository.
The API returns a JSON with all information about the releases.
"""
repo_owner = self.server["owner"]
repo_name = self.server["repo"]
request = f"https://api.github.com/repos/{repo_owner}/{repo_name}/releases"
response = requests.get(request)
if response.status_code != 200:
logging.error(f"Could not retrieve the latest releases from github. Request error: {response.status_code}")
sys.exit(1)

# Iterate over the releases and get the one with the highest tag
releases = response.json()
return releases

def search_for_highest_tag(releases):
"""
Search for the highest tag in the releases.
This will be the previous release.
"""
tag_list = []
for release in releases:
try:
semver.VersionInfo.parse(release["tag_name"])
except ValueError:
logging.warning(f"Tag {release['tag_name']} is not a valid semver tag. Not considered for latest candidate.")
continue
tag_list.append(release["tag_name"])

tag_list.sort(key=semver.VersionInfo.parse, reverse=True)

Copy link
Preview

Copilot AI May 30, 2025

Choose a reason for hiding this comment

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

Ensure that tag_list is not empty before accessing its first element to avoid a potential IndexError if no valid semver tags are found. Consider adding a check to handle the empty list scenario gracefully.

Suggested change
if not tag_list:
logging.error("No valid semver tags found in the releases. Cannot determine the highest tag.")
return None

Copilot uses AI. Check for mistakes.

return tag_list[0]

releases = get_repo_releases()
return search_for_highest_tag(releases)


previous_tag = get_previous_release_tag()

# Create the url based on the server
if self.server["type"] == "github":
url = (
Expand All @@ -481,7 +534,9 @@ def __get_latest_release_package_index(self):
+ "/"
+ str(self.server["repo"])
+ "/"
+ "releases/latest/download/"
+ "releases/download/"
+ str(previous_tag)
+ "/"
+ str(self.pckg_index_name)
+ ".json"
)
Expand Down Expand Up @@ -597,7 +652,6 @@ def build_release_assets(
rel_conf_yml,
core_root_path,
pkg_build_path,
ver_check=True,
inc_previous_releases=True,
):
"""
Expand All @@ -609,7 +663,6 @@ def build_release_assets(
- rel_conf_yml: The path to the release configuration yml file.
- core_root_path: The path to the arduino core root directory.
- pkg_build_path: The path to build the package.
- ver_check: Assert that versions across core metafiles and git matches.
- inc_previous_releases: Include previous releases in the package index.
"""

Expand All @@ -619,10 +672,6 @@ def build_release_assets(
# Get the package version information
package_version = PackageVersion()

# Validate the version consistency
if ver_check:
package_version.check_consistency(os.path.join(core_root_path, "platform.txt"))

# Create the package directory
core_package = CorePackage(
release_config.package_name,
Expand Down Expand Up @@ -659,7 +708,7 @@ def __init__(self):
"""
# Get the script name without .py extension
self.package_tool_name = os.path.splitext(os.path.basename(__file__))[0]
self.package_tool_version = "0.1.0"
self.package_tool_version = "0.2.0"
self.__create_parser()

args = self.parser.parse_args(namespace=argparse.Namespace(package_parser=self))
Expand Down Expand Up @@ -709,7 +758,6 @@ def __main_parser_func(self, args):
args.config_yml,
args.root_path,
args.build_path,
args.no_version_check,
args.no_previous_releases,
)

Expand Down Expand Up @@ -768,14 +816,6 @@ def __create_parser(self):
help='Path to build the package. Default is the "build/" directory in the arduino core path',
)

# Argument for version check
self.parser.add_argument(
"--no-version-check",
action="store_false",
default=True,
help="Do not check the version consistency.",
)

# Argument for previous releases
self.parser.add_argument(
"--no-previous-releases",
Expand Down
Loading