From f51b61b2f68c7e7c78610f91ebae4408349dd3c8 Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Thu, 30 Oct 2025 20:43:57 +0200 Subject: [PATCH 1/4] Use GitHub API to fetch latest release tag --- .github/workflows/developer-guide-docs.yml | 14 ++ .../developer-guide/About-This-Guide.asciidoc | 2 +- docs/developer-guide/Index.asciidoc | 2 + .../Maven-Getting-Started.adoc | 8 +- .../Maven-Project-Workflow.asciidoc | 7 +- .../Maven-Updating-Codename-One.adoc | 4 +- .../Working-With-CodenameOne-Sources.asciidoc | 4 +- docs/developer-guide/developer-guide.asciidoc | 34 ++-- .../determine_release_version.py | 150 ++++++++++++++++++ 9 files changed, 198 insertions(+), 27 deletions(-) create mode 100755 scripts/developer-guide/determine_release_version.py diff --git a/.github/workflows/developer-guide-docs.yml b/.github/workflows/developer-guide-docs.yml index cb1fd62212..48271df408 100644 --- a/.github/workflows/developer-guide-docs.yml +++ b/.github/workflows/developer-guide-docs.yml @@ -114,6 +114,20 @@ jobs: echo "REV_HUMAN_DATE=$REV_HUMAN_DATE" } >> "$GITHUB_ENV" + - name: Determine Codename One release version + run: | + set -euo pipefail + VERSION="$(python3 scripts/developer-guide/determine_release_version.py)" + if [ -z "$VERSION" ]; then + echo "Unable to determine Codename One release version" >&2 + exit 1 + fi + echo "Using Codename One release version: $VERSION" >&2 + { + echo "CN1_RELEASE_VERSION=$VERSION" + echo "CN1_PLUGIN_RELEASE_VERSION=$VERSION" + } >> "$GITHUB_ENV" + - name: Render publication cover artwork run: | set -euo pipefail diff --git a/docs/developer-guide/About-This-Guide.asciidoc b/docs/developer-guide/About-This-Guide.asciidoc index a0959e78ae..cba95757f3 100644 --- a/docs/developer-guide/About-This-Guide.asciidoc +++ b/docs/developer-guide/About-This-Guide.asciidoc @@ -17,7 +17,7 @@ While this guide focuses on tutorial and conceptual material, the complete API r This document includes content from multiple authors and community wiki edits. If you edit pages within the guide feel free to add your name here alphabetized by surname: -- https://github.com/codenameone/[Shai Almog] +- https://github.com/shai-almog[Shai Almog] - https://github.com/Isborg[Ismael Baum] - https://twitter.com/ericcoolmandev[Eric Coolman] - http://github.com/chen-fishbein/[Chen Fishbein] diff --git a/docs/developer-guide/Index.asciidoc b/docs/developer-guide/Index.asciidoc index 6b8d070796..bc8e85d312 100644 --- a/docs/developer-guide/Index.asciidoc +++ b/docs/developer-guide/Index.asciidoc @@ -39,6 +39,8 @@ IMPORTANT: Codename One doesn't send source code to the build cloud, only compil The build servers allow building native iOS Apps without a Mac and native Windows apps without a Windows machine. They remove the need to install/update complex toolchains and simplify the process of building a native app to a right click. +Even though the build servers streamline delivery, Codename One also supports fully local builds. You can install the toolchain on your own hardware and follow the workflows in <> and <> to compile, package, and test apps without leaving your desktop environment. + E.g.: Since building native iOS applications requires a Mac OS X machine with a recent version of xcode Codename One maintains such machines in the cloud. When developers send an iOS build such a Mac will be used to generate C source code using https://github.com/codenameone/CodenameOne/tree/master/vm[ParparVM] and it will then compile the C source code using xcode & sign the resulting binary using xcode. You can install the binary to your device or build a distribution binary for the appstore. Since C code is generated it also means that your app will be "future proof" in a case of changes from Apple. You can also inject Objective-C native code into the app while keeping it 100% portable thanks to the "native interfaces" capability of Codename One. Subscribers can receive the C source code back using the include sources feature of Codename One and use those sources for benchmarking, debugging on devices etc. diff --git a/docs/developer-guide/Maven-Getting-Started.adoc b/docs/developer-guide/Maven-Getting-Started.adoc index b36fb39f09..8de7b3d5b3 100644 --- a/docs/developer-guide/Maven-Getting-Started.adoc +++ b/docs/developer-guide/Maven-Getting-Started.adoc @@ -78,7 +78,7 @@ Here is an example which generates a project based on the bare-bones kotlin temp [source,bash] ---- -mvn com.codenameone:codenameone-maven-plugin:7.0.19:generate-app-project \ +mvn com.codenameone:codenameone-maven-plugin:{cn1-plugin-release-version}:generate-app-project \ -DarchetypeGroupId=$archetypeGroupId \ -DarchetypeArtifactId=$archetypeArtifactId \ -DarchetypeVersion=$archetypeVersion \ @@ -96,7 +96,7 @@ Like the `archetype:generate` goal, this will create the project in a directory Some notes here: -. The `com.codenameone:codenameone-maven-plugin:7.0.19:generate-app-project` argument is the fully-qualified goal name for the `generate-app-project`. This is necessary since we aren't running this goal in the context of any existing project. You should adjust the version number (`7.0.19`) to reflect the https://search.maven.org/search?q=a:codenameone-maven-plugin[latest available Codename One version on Maven Central]. +. The `com.codenameone:codenameone-maven-plugin:{cn1-plugin-release-version}:generate-app-project` argument is the fully-qualified goal name for the `generate-app-project`. This is necessary since we aren't running this goal in the context of any existing project. You should adjust the version number (`{cn1-plugin-release-version}`) to reflect the https://search.maven.org/search?q=a:codenameone-maven-plugin[latest available Codename One version on Maven Central]. . The `archetypeGroupId`, `archetypeArtifactId`, and `archetypeVersion` parameters are the same as when using the `archetype:generate` goal, and they will (almost) always refer to the <>. . The `groupId`, `artifactId`, and `version` work the same as for the `archetype:generate` goal. That is, that they specify the coordinates for your newly created project. . The `mainName` specifies the Main class name for your app. This is just the class name, and should not include the full package. E.g. "MyApp", not "com.example.MyApp" @@ -121,7 +121,7 @@ A minimal invocation of this goal would look like: # Specify your the version of the codenameone-maven-plugin. # Find the latest version at # https://search.maven.org/search?q=a:codenameone-maven-plugin -CN1VERSION=7.0.19 +CN1VERSION={cn1-plugin-release-version} mvn com.codenameone:codenameone-maven-plugin:$CN1VERSION:generate-app-project \ -DgroupId=YOUR_GROUP_ID \ -DartifactId=YOUR_ARTIFACT_ID \ @@ -212,7 +212,7 @@ The following is a bash script that uses curl to download this project as a zip [source,bash] ---- -CN1_VERSION=7.0.19 +CN1_VERSION={cn1-plugin-release-version} curl -L https://github.com/codenameone/KitchenSink/archive/v1.0-cn7.0.11.zip > master.zip unzip master.zip rm master.zip diff --git a/docs/developer-guide/Maven-Project-Workflow.asciidoc b/docs/developer-guide/Maven-Project-Workflow.asciidoc index 4c6381a32d..8f9690f7c4 100644 --- a/docs/developer-guide/Maven-Project-Workflow.asciidoc +++ b/docs/developer-guide/Maven-Project-Workflow.asciidoc @@ -8,8 +8,5 @@ include::Maven-Getting-Started.adoc[leveloffset=+1] include::Maven-Updating-Codename-One.adoc[leveloffset=+1] include::Maven-Project-Templates.adoc[leveloffset=+1] include::Maven-Creating-CN1Libs.adoc[leveloffset=+1] -include::Maven-Appendix-Archetypes.adoc[leveloffset=+1] -include::Maven-Appendix-Goals.adoc[leveloffset=+1] -include::Maven-Appendix-API.adoc[leveloffset=+1] -include::Maven-Appendix-Control-Center.adoc[leveloffset=+1] -include::Maven-Appendix-Rich-Properties.adoc[leveloffset=+1] + +// The detailed Maven appendices now live alongside the other appendices at the end of the guide. diff --git a/docs/developer-guide/Maven-Updating-Codename-One.adoc b/docs/developer-guide/Maven-Updating-Codename-One.adoc index b87b62134c..6827e793f3 100644 --- a/docs/developer-guide/Maven-Updating-Codename-One.adoc +++ b/docs/developer-guide/Maven-Updating-Codename-One.adoc @@ -50,8 +50,8 @@ E.g. Open the `pom.xml` file, and look for the following: [source,xml] ---- -7.0.19 -7.0.19 +{cn1-plugin-release-version} +{cn1-release-version} ---- Change these values to reflect the latest version of the `codenameone-maven-plugin` found https://search.maven.org/artifact/com.codenameone/codenameone-maven-plugin[here]. diff --git a/docs/developer-guide/Working-With-CodenameOne-Sources.asciidoc b/docs/developer-guide/Working-With-CodenameOne-Sources.asciidoc index d1c4b767c4..5bad453688 100644 --- a/docs/developer-guide/Working-With-CodenameOne-Sources.asciidoc +++ b/docs/developer-guide/Working-With-CodenameOne-Sources.asciidoc @@ -81,8 +81,8 @@ project), adjust the `pom.xml` properties to reference your snapshot version: ---- - 7.0.21-SNAPSHOT - 7.0.21-SNAPSHOT + {cn1-snapshot-version} + {cn1-snapshot-version} ---- diff --git a/docs/developer-guide/developer-guide.asciidoc b/docs/developer-guide/developer-guide.asciidoc index 92b8fc0bbd..5a18de4a51 100644 --- a/docs/developer-guide/developer-guide.asciidoc +++ b/docs/developer-guide/developer-guide.asciidoc @@ -28,7 +28,17 @@ :copyright: Codename One, all rights reserved :publication-type: book :producer: Codename One Ltd. -:partnums: +:cn1-release-version: {env:CN1_RELEASE_VERSION} +ifeval::["{cn1-release-version}" == ""] +:cn1-release-version: 7.0.21 +endif::[] + +:cn1-plugin-release-version: {env:CN1_PLUGIN_RELEASE_VERSION} +ifeval::["{cn1-plugin-release-version}" == ""] +:cn1-plugin-release-version: {cn1-release-version} +endif::[] + +:cn1-snapshot-version: {cn1-release-version}-SNAPSHOT = {doctitle} {author} @@ -37,12 +47,8 @@ toc::[] include::About-This-Guide.asciidoc[] -= Part I. Project Setup - include::Maven-Project-Workflow.asciidoc[] -= Part II. Foundations - include::Index.asciidoc[] include::basics.asciidoc[] @@ -53,8 +59,6 @@ include::Advanced-Theming.asciidoc[] include::css.asciidoc[] -= Part III. User Interface and Experience - include::The-Components-Of-Codename-One.asciidoc[] include::Animations.asciidoc[] @@ -63,8 +67,6 @@ include::The-EDT---Event-Dispatch-Thread.asciidoc[] include::graphics.asciidoc[] -= Part IV. Application Services - include::Events.asciidoc[] include::io.asciidoc[] @@ -77,8 +79,6 @@ include::performance.asciidoc[] include::Monetization.asciidoc[] -= Part V. Platform and Deployment - include::Advanced-Topics-Under-The-Hood.asciidoc[] include::security.asciidoc[] @@ -91,10 +91,18 @@ include::Working-With-Javascript.asciidoc[] include::Working-with-Mac-OS-X.asciidoc[] -= Part VI. Contributing and Appendices - include::Working-With-CodenameOne-Sources.asciidoc[] +include::Maven-Appendix-Archetypes.adoc[] + +include::Maven-Appendix-Goals.adoc[] + +include::Maven-Appendix-API.adoc[] + +include::Maven-Appendix-Control-Center.adoc[] + +include::Maven-Appendix-Rich-Properties.adoc[] + = Historical Reference include::Working-with-UWP.asciidoc[] diff --git a/scripts/developer-guide/determine_release_version.py b/scripts/developer-guide/determine_release_version.py new file mode 100755 index 0000000000..1356b821f5 --- /dev/null +++ b/scripts/developer-guide/determine_release_version.py @@ -0,0 +1,150 @@ +#!/usr/bin/env python3 +"""Determine the current Codename One release version for documentation builds.""" +from __future__ import annotations + +import json +import os +import re +import subprocess +import sys +import xml.etree.ElementTree as ET +from pathlib import Path +from typing import Iterable, List +from urllib.error import HTTPError, URLError +from urllib.request import Request, urlopen + + +def _sanitize_tag(value: str) -> str: + value = value.strip() + if value.lower().startswith("refs/tags/"): + value = value[10:] + if value.lower().startswith("tags/"): + value = value[5:] + if value and value[0] in {"v", "V"} and value[1:2].isdigit(): + value = value[1:] + return value.strip() + + +def _parse_version_components(version: str) -> List[int]: + return [int(part) for part in version.split(".")] + + +def release_tag_from_event() -> str: + event_path = os.environ.get("GITHUB_EVENT_PATH") + if event_path: + event_file = Path(event_path) + if event_file.is_file(): + try: + data = json.loads(event_file.read_text()) + except json.JSONDecodeError: + data = {} + release = data.get("release") or {} + tag = release.get("tag_name") or release.get("target_commitish") or "" + if tag: + return _sanitize_tag(tag) + for key in ("GITHUB_REF_NAME", "GITHUB_REF"): + value = os.environ.get(key) + if value: + sanitized = _sanitize_tag(value) + if sanitized: + return sanitized + return "" + + +def latest_release_from_api() -> str: + repository = os.environ.get("GITHUB_REPOSITORY") + if not repository: + return "" + api_base = os.environ.get("GITHUB_API_URL", "https://api.github.com") + url = f"{api_base.rstrip('/')}/repos/{repository}/releases/latest" + headers = { + "Accept": "application/vnd.github+json", + "User-Agent": "codenameone-docs-release-version", + } + token = os.environ.get("GITHUB_TOKEN") or os.environ.get("GH_TOKEN") + if token: + headers["Authorization"] = f"Bearer {token}" + request = Request(url, headers=headers) + try: + with urlopen(request, timeout=10) as response: + if response.status != 200: + return "" + try: + payload = json.load(response) + except json.JSONDecodeError: + return "" + except (HTTPError, URLError, TimeoutError): + return "" + tag = payload.get("tag_name") + if not tag: + return "" + return _sanitize_tag(str(tag)) + + +def latest_git_tag() -> str: + try: + subprocess.run( + ["git", "fetch", "--tags", "--force"], + check=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) + except (subprocess.CalledProcessError, FileNotFoundError): + pass + try: + result = subprocess.run( + ["git", "tag", "--list", "v*"], + check=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True, + ) + except (subprocess.CalledProcessError, FileNotFoundError): + return "" + tags: Iterable[str] = (_sanitize_tag(line) for line in result.stdout.splitlines()) + numeric_tags = [tag for tag in tags if re.fullmatch(r"\d+(?:\.\d+)*", tag)] + if not numeric_tags: + return "" + numeric_tags.sort(key=_parse_version_components) + return numeric_tags[-1] + + +def version_from_pom(root: Path) -> str: + pom_path = root / "maven" / "pom.xml" + if not pom_path.is_file(): + return "" + try: + tree = ET.parse(pom_path) + except ET.ParseError: + return "" + namespace = {"mvn": "http://maven.apache.org/POM/4.0.0"} + version_element = tree.getroot().find("mvn:version", namespace) + if version_element is None: + return "" + version = (version_element.text or "").strip() + if not version: + return "" + if version.endswith("-SNAPSHOT"): + version = version[: -len("-SNAPSHOT")] + return version + + +def main() -> int: + repo_root = Path(__file__).resolve().parents[2] + + for candidate in (release_tag_from_event, latest_release_from_api, latest_git_tag): + version = candidate() + if version: + print(version) + return 0 + + version = version_from_pom(repo_root) + if version: + print(version) + return 0 + + return 1 + + +if __name__ == "__main__": + sys.exit(main()) From 17680618c80fad00a9f83e62881e7ba3a1990b67 Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Thu, 30 Oct 2025 20:58:14 +0200 Subject: [PATCH 2/4] Filter non-version refs in release helper --- .../developer-guide/determine_release_version.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/scripts/developer-guide/determine_release_version.py b/scripts/developer-guide/determine_release_version.py index 1356b821f5..c81af4d572 100755 --- a/scripts/developer-guide/determine_release_version.py +++ b/scripts/developer-guide/determine_release_version.py @@ -14,6 +14,9 @@ from urllib.request import Request, urlopen +VERSION_PATTERN = re.compile(r"\d+(?:\.\d+)*$") + + def _sanitize_tag(value: str) -> str: value = value.strip() if value.lower().startswith("refs/tags/"): @@ -25,6 +28,10 @@ def _sanitize_tag(value: str) -> str: return value.strip() +def _looks_like_version(tag: str) -> bool: + return bool(tag and VERSION_PATTERN.fullmatch(tag)) + + def _parse_version_components(version: str) -> List[int]: return [int(part) for part in version.split(".")] @@ -41,12 +48,14 @@ def release_tag_from_event() -> str: release = data.get("release") or {} tag = release.get("tag_name") or release.get("target_commitish") or "" if tag: - return _sanitize_tag(tag) + sanitized = _sanitize_tag(tag) + if _looks_like_version(sanitized): + return sanitized for key in ("GITHUB_REF_NAME", "GITHUB_REF"): value = os.environ.get(key) if value: sanitized = _sanitize_tag(value) - if sanitized: + if _looks_like_version(sanitized): return sanitized return "" From 33c934078d49d97568e2d6bdb1bc600bc91e0056 Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Sun, 2 Nov 2025 04:45:12 +0200 Subject: [PATCH 3/4] Enable version attribute substitution in Maven docs --- docs/developer-guide/Maven-Getting-Started.adoc | 6 +++--- docs/developer-guide/Maven-Updating-Codename-One.adoc | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/developer-guide/Maven-Getting-Started.adoc b/docs/developer-guide/Maven-Getting-Started.adoc index 8de7b3d5b3..802c56ff16 100644 --- a/docs/developer-guide/Maven-Getting-Started.adoc +++ b/docs/developer-guide/Maven-Getting-Started.adoc @@ -76,7 +76,7 @@ The https://shannah.github.io/cn1app-archetype-kotlin-template/getting-started.h Here is an example which generates a project based on the bare-bones kotlin template: -[source,bash] +[source,bash,subs="+attributes"] ---- mvn com.codenameone:codenameone-maven-plugin:{cn1-plugin-release-version}:generate-app-project \ -DarchetypeGroupId=$archetypeGroupId \ @@ -116,7 +116,7 @@ If you have an existing Codename One application project that uses the old Ant p A minimal invocation of this goal would look like: -[source,bash] +[source,bash,subs="+attributes"] ---- # Specify your the version of the codenameone-maven-plugin. # Find the latest version at @@ -137,7 +137,7 @@ After building the project, try running it to make sure that the migration worke ==== Command Line -[source,bash] +[source,bash,subs="+attributes"] ---- cd myapp ./run.sh diff --git a/docs/developer-guide/Maven-Updating-Codename-One.adoc b/docs/developer-guide/Maven-Updating-Codename-One.adoc index 6827e793f3..91e59662dc 100644 --- a/docs/developer-guide/Maven-Updating-Codename-One.adoc +++ b/docs/developer-guide/Maven-Updating-Codename-One.adoc @@ -48,7 +48,7 @@ You can also update your Codename One dependencies manually by modifying the `cn E.g. Open the `pom.xml` file, and look for the following: -[source,xml] +[source,xml,subs="+attributes"] ---- {cn1-plugin-release-version} {cn1-release-version} From 3e7883e660312ccc9cb0235c35302978962f5c6c Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Mon, 3 Nov 2025 04:41:21 +0200 Subject: [PATCH 4/4] Fix release version fallbacks and enable attribute substitution --- docs/developer-guide/Maven-Getting-Started.adoc | 2 +- docs/developer-guide/developer-guide.asciidoc | 10 +++------- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/docs/developer-guide/Maven-Getting-Started.adoc b/docs/developer-guide/Maven-Getting-Started.adoc index 802c56ff16..d14dcba528 100644 --- a/docs/developer-guide/Maven-Getting-Started.adoc +++ b/docs/developer-guide/Maven-Getting-Started.adoc @@ -210,7 +210,7 @@ Let's consider a concrete example, now. Download the KitchenSink Ant project fr The following is a bash script that uses curl to download this project as a zip file, and then converts it to a fully-functional Maven project. -[source,bash] +[source,bash,subs="+attributes"] ---- CN1_VERSION={cn1-plugin-release-version} curl -L https://github.com/codenameone/KitchenSink/archive/v1.0-cn7.0.11.zip > master.zip diff --git a/docs/developer-guide/developer-guide.asciidoc b/docs/developer-guide/developer-guide.asciidoc index 5a18de4a51..c30d018b53 100644 --- a/docs/developer-guide/developer-guide.asciidoc +++ b/docs/developer-guide/developer-guide.asciidoc @@ -28,15 +28,11 @@ :copyright: Codename One, all rights reserved :publication-type: book :producer: Codename One Ltd. -:cn1-release-version: {env:CN1_RELEASE_VERSION} -ifeval::["{cn1-release-version}" == ""] -:cn1-release-version: 7.0.21 -endif::[] +:cn1-release-version: 7.0.210 +ifdef::env-CN1_RELEASE_VERSION[:cn1-release-version: {env:CN1_RELEASE_VERSION}] -:cn1-plugin-release-version: {env:CN1_PLUGIN_RELEASE_VERSION} -ifeval::["{cn1-plugin-release-version}" == ""] :cn1-plugin-release-version: {cn1-release-version} -endif::[] +ifdef::env-CN1_PLUGIN_RELEASE_VERSION[:cn1-plugin-release-version: {env:CN1_PLUGIN_RELEASE_VERSION}] :cn1-snapshot-version: {cn1-release-version}-SNAPSHOT