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..d14dcba528 100644 --- a/docs/developer-guide/Maven-Getting-Started.adoc +++ b/docs/developer-guide/Maven-Getting-Started.adoc @@ -76,9 +76,9 @@ 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: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" @@ -116,12 +116,12 @@ 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 # 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 \ @@ -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 @@ -210,9 +210,9 @@ 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=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..91e59662dc 100644 --- a/docs/developer-guide/Maven-Updating-Codename-One.adoc +++ b/docs/developer-guide/Maven-Updating-Codename-One.adoc @@ -48,10 +48,10 @@ 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"] ---- -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..c30d018b53 100644 --- a/docs/developer-guide/developer-guide.asciidoc +++ b/docs/developer-guide/developer-guide.asciidoc @@ -28,7 +28,13 @@ :copyright: Codename One, all rights reserved :publication-type: book :producer: Codename One Ltd. -:partnums: +:cn1-release-version: 7.0.210 +ifdef::env-CN1_RELEASE_VERSION[:cn1-release-version: {env:CN1_RELEASE_VERSION}] + +:cn1-plugin-release-version: {cn1-release-version} +ifdef::env-CN1_PLUGIN_RELEASE_VERSION[:cn1-plugin-release-version: {env:CN1_PLUGIN_RELEASE_VERSION}] + +:cn1-snapshot-version: {cn1-release-version}-SNAPSHOT = {doctitle} {author} @@ -37,12 +43,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 +55,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 +63,6 @@ include::The-EDT---Event-Dispatch-Thread.asciidoc[] include::graphics.asciidoc[] -= Part IV. Application Services - include::Events.asciidoc[] include::io.asciidoc[] @@ -77,8 +75,6 @@ include::performance.asciidoc[] include::Monetization.asciidoc[] -= Part V. Platform and Deployment - include::Advanced-Topics-Under-The-Hood.asciidoc[] include::security.asciidoc[] @@ -91,10 +87,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..c81af4d572 --- /dev/null +++ b/scripts/developer-guide/determine_release_version.py @@ -0,0 +1,159 @@ +#!/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 + + +VERSION_PATTERN = re.compile(r"\d+(?:\.\d+)*$") + + +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 _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(".")] + + +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: + 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 _looks_like_version(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())