diff --git a/.github/ci.yaml b/.github/ci.yaml index e97c623..d8c3e4a 100644 --- a/.github/ci.yaml +++ b/.github/ci.yaml @@ -25,6 +25,7 @@ images: - directory: "python/3.10-22.04" - directory: "python/3.12-24.04" - directory: "python/3.13-25.10" + - directory: "python/3.14-26.04" ## Example of specifying different registries for a specific rock # - directory: "my-rock-name/0.1-24.04" diff --git a/.github/workflows/image-internal.yaml b/.github/workflows/image-internal.yaml new file mode 100644 index 0000000..8ca0965 --- /dev/null +++ b/.github/workflows/image-internal.yaml @@ -0,0 +1,137 @@ +# This workflow is intended for internal Canonical repositories only. +# It includes steps that rely on internal actions or secrets, +# and it performs additional checks to prevent accidental usage in public repositories. +# If you are working in a public repository, please use the .github/workflows/image.yaml workflow instead. +# +name: Build and Publish Rocks (Internal) + +on: + # Uncomment these lines to enable the workflow on push and pull_request events + # push: + # branches: [main] + # pull_request: + # branches: [main] + workflow_dispatch: + +jobs: + + prepare: + runs-on: ubuntu-latest + outputs: + ghcr-upload: ${{ steps.read-ci-config.outputs.ghcr-upload }} + build-matrix: ${{ steps.read-ci-config.outputs.build-matrix }} + upload-matrix: ${{ steps.read-ci-config.outputs.upload-matrix }} + steps: + - name: Pre-Check + if: | + github.repository_owner != 'canonical' || + github.event.repository.visibility == 'public' + run: | + echo "::error::This workflow is intended for internal repositories only. Please use the .github/workflows/image.yaml workflow instead." + exit 1 + + - name: Checkout Repository + uses: actions/checkout@v5 + + - name: Read .github/ci.yaml + id: read-ci-config + uses: canonical/rocks-template-actions/actions/read-ci-config@v1 + + + build-internal: + needs: [prepare] + strategy: + fail-fast: true + matrix: ${{ fromJSON(needs.prepare.outputs.build-matrix) }} + uses: canonical/oci-factory-internal/.github/workflows/Build-Rock.yaml@main + with: + rock-repo: ${{ github.event.pull_request.head.repo.full_name || github.repository }} + rock-repo-commit: ${{ github.ref }} + rockfile-directory: ${{ matrix.directory }} + oci-archive-name: ${{ matrix.artifact-name }} + secrets: + host-github-token: ${{ secrets.GITHUB_TOKEN }} + source-github-token: ${{ secrets.REPO_CLONER_TOKEN }} + + + test: + needs: [prepare, build-internal] + strategy: + fail-fast: false + matrix: ${{ fromJson(needs.prepare.outputs.build-matrix) }} + uses: canonical/oci-factory/.github/workflows/Test-Rock.yaml@main + with: + oci-archive-name: ${{ matrix.artifact-name }} + + + upload-ghcr: + needs: [prepare, test] + runs-on: ubuntu-latest + if: | + needs.prepare.outputs.ghcr-upload == 'true' && + github.event_name != 'pull_request' && + github.ref == 'refs/heads/main' + strategy: + matrix: ${{ fromJSON(needs.prepare.outputs.build-matrix) }} + fail-fast: false + permissions: + packages: write + steps: + - name: Upload Rock to GHCR + uses: canonical/oci-factory/.github/actions/upload-rock@main + with: + artifact_name: ${{ matrix.artifact-name }} + tags: ${{ matrix.tag }} + name: ${{ matrix.name }} + registry: ghcr.io/${{ github.repository }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + + upload-registries: + needs: [prepare, test] + runs-on: ubuntu-latest + if: | + needs.prepare.outputs.upload-matrix != '{"include": []}' && + github.event_name != 'pull_request' && + github.ref == 'refs/heads/main' + strategy: + matrix: ${{ fromJSON(needs.prepare.outputs.upload-matrix) }} + fail-fast: false + steps: + - name: Prepare ECR Session Token + if: ${{ contains(matrix.registry-auth-method, 'ecr') }} + id: get-ecr-token + env: + AWS_ACCESS_KEY_ID: ${{ secrets[matrix.registry-auth-username] }} + AWS_SECRET_ACCESS_KEY: ${{ secrets[matrix.registry-auth-password] }} + run: | + session_token=$(aws ${{ matrix.registry-auth-method }} \ + get-login-password \ + --region ${{ matrix.registry-auth-region }} \ + ) + echo "::add-mask::$session_token" + echo "aws-session-token=$session_token" >> $GITHUB_OUTPUT + + - name: Upload Rock to ECR + uses: canonical/oci-factory/.github/actions/upload-rock@main + if: ${{ contains(matrix.registry-auth-method, 'ecr') }} + with: + artifact_name: ${{ matrix.artifact-name }} + tags: ${{ matrix.tag }} + name: ${{ matrix.name }} + registry: ${{ matrix.registry-uri }} + username: AWS + password: ${{ steps.get-ecr-token.outputs.aws-session-token }} + + - name: Upload Rock to Registry + uses: canonical/oci-factory/.github/actions/upload-rock@main + if: matrix.registry-auth-method == 'basic' + with: + artifact_name: ${{ matrix.artifact-name }} + tags: ${{ matrix.tag }} + name: ${{ matrix.name }} + registry: ${{ matrix.registry-uri }} + username: ${{ secrets[matrix.registry-auth-username] }} + password: ${{ secrets[matrix.registry-auth-password] }} + diff --git a/.github/workflows/publish.yaml b/.github/workflows/image.yaml similarity index 52% rename from .github/workflows/publish.yaml rename to .github/workflows/image.yaml index 3cb2a58..6015886 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/image.yaml @@ -1,4 +1,4 @@ -name: Build and Publish Rocks to GHCR +name: Build and Publish Rocks on: push: @@ -8,102 +8,97 @@ on: workflow_dispatch: jobs: - - read-config: + prepare: runs-on: ubuntu-latest outputs: ghcr-upload: ${{ steps.read-ci-config.outputs.ghcr-upload }} - ghcr-cve-scan: ${{ steps.read-ci-config.outputs.ghcr-cve-scan }} build-matrix: ${{ steps.read-ci-config.outputs.build-matrix }} upload-matrix: ${{ steps.read-ci-config.outputs.upload-matrix }} + arch-map: ${{ steps.set-map.outputs.arch-map }} steps: - - name: Checkout repository + - name: Checkout Repository uses: actions/checkout@v5 - name: Read .github/ci.yaml id: read-ci-config uses: canonical/rocks-template-actions/actions/read-ci-config@v1 - get-runners: - runs-on: ubuntu-latest - outputs: - arch-map: ${{ steps.set-map.outputs.arch-map }} - steps: - - id: set-map + - name: Set Architecture Map + id: set-map run: | - if [[ "${{ github.repository_owner }}" == "canonical" ]]; then - echo 'arch-map={"amd64":["noble","X64","large"],"arm64":["noble","ARM64","large"]}' >> $GITHUB_OUTPUT - else + if [[ "${{ github.repository_owner }}" != "canonical" ]]; then echo 'arch-map={"amd64":["ubuntu-24.04"],"arm64":["ubuntu-24.04-arm"]}' >> $GITHUB_OUTPUT fi - # Why do we need this duplicate job and reusable Build-Rock workflow? - # - the Build-Rock wf may need an an environment setup that relies on an internal action - # - to call an internal action, the repo must also be internal/private - # - but the OCI Factory is not internal/private, so that internal action cannot be called from there - # build-internal: - # needs: [get-runners, read-config] - # if: github.event.repository.visibility != 'public' - # strategy: - # fail-fast: true - # matrix: ${{ fromJSON(needs.read-config.outputs.build-matrix) }} - # uses: canonical/oci-factory-internal/.github/workflows/Build-Rock.yaml@main - # with: - # rock-repo: ${{ github.repository }} - # rock-repo-commit: ${{ github.ref }} - # rockfile-directory: ${{ matrix.directory }} - # oci-archive-name: ${{ matrix.artifact-name }} - # arch-map: ${{ needs.get-runners.outputs.arch-map }} - # secrets: - # host-github-token: ${{ secrets.GITHUB_TOKEN }} - # source-github-token: ${{ secrets.REPO_CLONER_TOKEN }} + # TODO: remove once the pro-feature is stable in rockcraft + # Warn the user if using tests in a pro enabled build + - name: Check Pro and Test Incompatibility + run: | + build_matrix='${{ steps.read-ci-config.outputs.build-matrix }}' + + # Use jq to iterate over the 'include' array + echo "$build_matrix" | jq -c '.include[]' | while read -r row; do + directory=$(echo "$row" | jq -r '.["directory"] // "unknown"') + pro_services=$(echo "$row" | jq -r '.["pro-services"] // ""') + run_tests=$(echo "$row" | jq -r '.["run-tests"] // "false"') + + if [[ -n "$pro_services" && "$run_tests" == "true" ]]; then + echo "::warning::Tests for Pro Services are currently not supported. Rockcraft tests will be skipped for ${directory}." + fi + done + build: - needs: [get-runners, read-config] - if: github.event.repository.visibility == 'public' + needs: [prepare] strategy: - matrix: ${{ fromJSON(needs.read-config.outputs.build-matrix) }} + matrix: ${{ fromJSON(needs.prepare.outputs.build-matrix) }} uses: canonical/oci-factory/.github/workflows/Build-Rock.yaml@main with: rock-repo: ${{ github.event.pull_request.head.repo.full_name || github.repository }} rock-repo-commit: ${{ github.head_ref || github.ref_name }} rockfile-directory: ${{ matrix.directory }} oci-archive-name: ${{ matrix.artifact-name }} - arch-map: ${{ needs.get-runners.outputs.arch-map }} + arch-map: ${{ needs.prepare.outputs.arch-map }} rockcraft-test: ${{ matrix.run-tests }} + pro-services: ${{ matrix.pro-services }} + secrets: + source-github-token: ${{ secrets.REPO_CLONER_TOKEN }} + pro-token: ${{ secrets[matrix.pro-token] }} + pro-artifact-passphrase: ${{ secrets[matrix.pro-artifact-passphrase] }} + test: - needs: [read-config, build] - # Uncomment these lines if using build-internal job and remove the line above - # needs: [read-config, build, build-internal] - # if: | - # always() && needs.read-config.result == 'success' && - # (needs.build-internal.result == 'success' && needs.build.result == 'skipped') || - # (needs.build-internal.result == 'skipped' && needs.build.result == 'success') + needs: [prepare, build] strategy: fail-fast: false - matrix: ${{ fromJson(needs.read-config.outputs.build-matrix) }} + matrix: ${{ fromJSON(needs.prepare.outputs.build-matrix) }} uses: canonical/oci-factory/.github/workflows/Test-Rock.yaml@main with: oci-archive-name: ${{ matrix.artifact-name }} + secrets: + pro-artifact-passphrase: ${{ secrets[matrix.pro-artifact-passphrase] }} + upload-ghcr: - needs: [read-config, test] - # Note: the !cancelled() is required to workaround the job being skipped - # due to a skipped job in the indirect dependency chain: - # https://github.com/orgs/community/discussions/25224 + needs: [prepare, test] + runs-on: ubuntu-latest if: | - github.event_name != 'pull_request' && !cancelled() && !failure() && - github.ref == 'refs/heads/main' && - needs.read-config.outputs.ghcr-upload == 'true' + needs.prepare.outputs.ghcr-upload == 'true' && + github.event_name != 'pull_request' && + github.ref == 'refs/heads/main' strategy: - matrix: ${{ fromJSON(needs.read-config.outputs.build-matrix) }} + matrix: ${{ fromJSON(needs.prepare.outputs.build-matrix) }} fail-fast: false - runs-on: ubuntu-latest permissions: packages: write steps: + - name: Pre-Check Pro Enabled Rocks + if: ${{ github.event.repository.visibility == 'public' && matrix.pro-services != '' }} + run: | + echo "::warning::Uploading Pro enabled rocks to GHCR is not allowed for public repositories." + - name: Upload Rock to GHCR + if: ${{ github.event.repository.visibility != 'public' || matrix.pro-services == '' }} uses: canonical/oci-factory/.github/actions/upload-rock@main with: artifact_name: ${{ matrix.artifact-name }} @@ -112,20 +107,19 @@ jobs: registry: ghcr.io/${{ github.repository }} username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} + decrypt-passphrase: ${{ secrets[matrix.pro-artifact-passphrase] }} + upload-registries: - needs: [read-config, test] - # Note: the !cancelled() is required to workaround the job being skipped - # due to a skipped job in the indirect dependency chain: - # https://github.com/orgs/community/discussions/25224 + needs: [prepare, test] + runs-on: ubuntu-latest if: | - github.event_name != 'pull_request' && !cancelled() && !failure() && - github.ref == 'refs/heads/main' && - needs.read-config.outputs.upload-matrix != '{"include": []}' + needs.prepare.outputs.upload-matrix != '{"include": []}' && + github.event_name != 'pull_request' && + github.ref == 'refs/heads/main' strategy: - matrix: ${{ fromJSON(needs.read-config.outputs.upload-matrix) }} + matrix: ${{ fromJSON(needs.prepare.outputs.upload-matrix) }} fail-fast: false - runs-on: ubuntu-latest steps: - name: Prepare ECR Session Token if: ${{ contains(matrix.registry-auth-method, 'ecr') }} @@ -151,6 +145,7 @@ jobs: registry: ${{ matrix.registry-uri }} username: AWS password: ${{ steps.get-ecr-token.outputs.aws-session-token }} + decrypt-passphrase: ${{ secrets[matrix.pro-artifact-passphrase] }} - name: Upload Rock to Registry uses: canonical/oci-factory/.github/actions/upload-rock@main @@ -162,4 +157,4 @@ jobs: registry: ${{ matrix.registry-uri }} username: ${{ secrets[matrix.registry-auth-username] }} password: ${{ secrets[matrix.registry-auth-password] }} - + decrypt-passphrase: ${{ secrets[matrix.pro-artifact-passphrase] }} diff --git a/.github/workflows/tag-rocks.yaml b/.github/workflows/tag-rocks.yaml deleted file mode 100644 index 20a317d..0000000 --- a/.github/workflows/tag-rocks.yaml +++ /dev/null @@ -1,65 +0,0 @@ -name: Tag Rocks - -on: - workflow_call: - outputs: - json_result: - description: "JSON array of rockcraft.yaml metadata" - value: ${{ jobs.tag-rocks.outputs.json_result }} - -jobs: - tag-rocks: - runs-on: ubuntu-latest - outputs: - json_result: ${{ steps.generate.outputs.json_result }} - - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Generate rocks list with its tag in JSON format - id: generate - run: | - output="[]" - while IFS= read -r -d '' file; do - name=$(yq '.name' "$file") - version=$(yq '.version' "$file") - base=$(yq '.base' "$file") - location=$(dirname "$file" | sed 's|^\./||') - channel="edge" - - if [[ "$base" == "bare" ]]; then - base=$(yq '.build-base' "$file") - fi - - base=$(echo "$base" | sed 's/[^0-9.]//g') - - if [[ "$version" == "latest" ]]; then - tag="latest" - echo "::warning file=$file::Using 'latest' as version — tag set to 'latest'" - else - tag="${version}-${base}_${channel}" - fi - - dir=$(dirname "$file") - if [[ -f "$dir/spread.yaml" ]]; then - test_flag=true - else - test_flag=false - fi - - entry=$(jq -n \ - --arg name "$name" \ - --arg version "$version" \ - --arg tag "$tag" \ - --arg location "$location" \ - --argjson test "$test_flag" \ - '{name: $name, version: $version, tag: $tag, location: $location, test: $test}') - - output=$(jq ". + [ $entry ]" <<< "$output") - done < <(find . -type f -name rockcraft.yaml -print0) - - echo "$output" > rocks.json - echo 'json_result<> "$GITHUB_OUTPUT" - cat rocks.json >> "$GITHUB_OUTPUT" - echo 'EOF' >> "$GITHUB_OUTPUT" diff --git a/README.md b/README.md index 2b243e0..646be25 100644 --- a/README.md +++ b/README.md @@ -14,3 +14,5 @@ repository. * [Python 3.11 (Ubuntu 22.04)](./python/3.11-22.04/rockcraft.yaml) * [Python 3.12 (Ubuntu 24.04)](./python/3.12-24.04/rockcraft.yaml) * [Python 3.13 (Ubuntu 25.04)](./python/3.13-25.04/rockcraft.yaml) +* [Python 3.13 (Ubuntu 25.10)](./python/3.13-25.10/rockcraft.yaml) +* [Python 3.14 (Ubuntu 26.04)](./python/3.14-26.04/rockcraft.yaml) diff --git a/python/3.10-22.04/spread/general/app_https-get/src/main.py b/python/3.10-22.04/spread/general/app_https-get/src/main.py index 815f5a7..897525e 100755 --- a/python/3.10-22.04/spread/general/app_https-get/src/main.py +++ b/python/3.10-22.04/spread/general/app_https-get/src/main.py @@ -1,4 +1,4 @@ -#!/usr/bin/python3.10 +#!/usr/bin/python3 import http.client import ssl diff --git a/python/3.11-22.04/spread/general/app_https-get/src/main.py b/python/3.11-22.04/spread/general/app_https-get/src/main.py index 07a6bd3..897525e 100755 --- a/python/3.11-22.04/spread/general/app_https-get/src/main.py +++ b/python/3.11-22.04/spread/general/app_https-get/src/main.py @@ -1,4 +1,4 @@ -#!/usr/bin/python3.11 +#!/usr/bin/python3 import http.client import ssl diff --git a/python/3.12-24.04/spread/general/app_https-get/src/main.py b/python/3.12-24.04/spread/general/app_https-get/src/main.py index 2766236..897525e 100755 --- a/python/3.12-24.04/spread/general/app_https-get/src/main.py +++ b/python/3.12-24.04/spread/general/app_https-get/src/main.py @@ -1,4 +1,4 @@ -#!/usr/bin/python3.12 +#!/usr/bin/python3 import http.client import ssl diff --git a/python/3.13-25.10/spread/general/app_https-get/src/main.py b/python/3.13-25.10/spread/general/app_https-get/src/main.py index 8785788..897525e 100755 --- a/python/3.13-25.10/spread/general/app_https-get/src/main.py +++ b/python/3.13-25.10/spread/general/app_https-get/src/main.py @@ -1,4 +1,4 @@ -#!/usr/bin/python3.13 +#!/usr/bin/python3 import http.client import ssl diff --git a/python/3.14-26.04/rockcraft.yaml b/python/3.14-26.04/rockcraft.yaml new file mode 100644 index 0000000..c0663ef --- /dev/null +++ b/python/3.14-26.04/rockcraft.yaml @@ -0,0 +1,36 @@ +name: python +version: "3.14" +summary: Chiseled Python 3.14 rock +description: | + Python is a high-level, general-purpose programming language. Its design + philosophy emphasizes code readability with the use of significant + indentation. Python is dynamically typed and garbage-collected. It supports + multiple programming paradigms, including structured, object-oriented and + functional programming. Read more on [python.org](https://www.python.org/). + + This image is a chiselled Ubuntu rock that contains only the Python runtime + and its standard libraries. +license: Python-2.0 +base: bare + +# TODO: Use 26.04 once rockcraft supports it. +build-base: devel + +run-user: _daemon_ + +platforms: + amd64: + arm64: + +parts: + python3: + plugin: nil + stage-packages: + - base-files_base + - base-files_release-info + - base-files_chisel + - ca-certificates_data + - libc6_libs + - libgcc-s1_libs + - openssl_config + - python3_standard diff --git a/python/3.14-26.04/spread.yaml b/python/3.14-26.04/spread.yaml new file mode 100644 index 0000000..f342025 --- /dev/null +++ b/python/3.14-26.04/spread.yaml @@ -0,0 +1,33 @@ +project: python-3.14-rock +backends: + craft: + type: craft + systems: + - ubuntu-24.04: + #- ubuntu-22.04: + +suites: + spread/general/: + summary: General integration tests + + prepare: | + sudo snap install docker + + # For 'rockcraft.skopeo' + sudo snap install --classic rockcraft + + # Wait for docker daemon to come online + sudo apt install --yes retry + retry --times=10 --delay 2 -- docker images + sudo apt remove --yes retry + + # Load the rock into docker + sudo rockcraft.skopeo --insecure-policy copy "oci-archive:$CRAFT_ARTIFACT" docker-daemon:python:3.14-26.04 + + restore: | + docker image rm --force python:3.14-26.04 + +exclude: + - .git + +kill-timeout: 1h diff --git a/python/3.14-26.04/spread/.extension b/python/3.14-26.04/spread/.extension new file mode 100755 index 0000000..0f862d7 --- /dev/null +++ b/python/3.14-26.04/spread/.extension @@ -0,0 +1,195 @@ +#!/bin/bash + +usage() { + echo "usage: $(basename "$0") [command]" + echo "valid commands:" + echo " allocate Create a backend instance to run tests on" + echo " discard Destroy a backend instance used to run tests" + echo " backend-prepare Set up the system to run tests" + echo " backend-restore Restore the system after the tests ran" + echo " backend-prepare-each Prepare the system before each test" + echo " backend-restore-each Restore the system after each test run" +} + +prepare() { + case "$SPREAD_SYSTEM" in + fedora*) + dnf update -y + dnf install -y snapd + while ! snap install snapd; do + echo "waiting for snapd..." + sleep 2 + done + ;; + debian*) + apt update + apt install -y snapd + while ! snap install snapd; do + echo "waiting for snapd..." + sleep 2 + done + ;; + ubuntu*) + apt update + ;; + esac + + snap wait system seed.loaded + snap refresh --hold + + if systemctl is-enabled unattended-upgades.service; then + systemctl stop unattended-upgrades.service + systemctl mask unattended-upgrades.service + fi +} + +restore() { + case "$SPREAD_SYSTEM" in + ubuntu* | debian*) + apt autoremove -y --purge + ;; + esac + + rm -Rf "$PROJECT_PATH" + mkdir -p "$PROJECT_PATH" +} + +prepare_each() { + true +} + +restore_each() { + true +} + +allocate_lxdvm() { + name=$(echo "$SPREAD_SYSTEM" | tr '[:punct:]' -) + system=$(echo "$SPREAD_SYSTEM" | tr / -) + if [[ "$system" =~ ^ubuntu- ]]; then + image="ubuntu:${system#ubuntu-}" + else + image="images:$(echo "$system" | tr - /)" + fi + + VM_NAME="${VM_NAME:-spread-${name}-${RANDOM}}" + DISK="${DISK:-20}" + CPU="${CPU:-4}" + MEM="${MEM:-8}" + + lxc launch --vm \ + "${image}" \ + "${VM_NAME}" \ + -c limits.cpu="${CPU}" \ + -c limits.memory="${MEM}GiB" \ + -d root,size="${DISK}GiB" + + while ! lxc exec "${VM_NAME}" -- true &>/dev/null; do sleep 0.5; done + lxc exec "${VM_NAME}" -- sed -i 's/^\s*#\?\s*\(PermitRootLogin\|PasswordAuthentication\)\>.*/\1 yes/' /etc/ssh/sshd_config + lxc exec "${VM_NAME}" -- bash -c "if [ -d /etc/ssh/sshd_config.d ]; then echo -e 'PermitRootLogin yes\nPasswordAuthentication yes' > /etc/ssh/sshd_config.d/00-spread.conf; fi" + lxc exec "${VM_NAME}" -- bash -c "echo root:${SPREAD_PASSWORD} | sudo chpasswd || true" + + # Print the instance address to stdout + ADDR="" + while [ -z "$ADDR" ]; do ADDR=$(lxc ls -f csv | grep "^${VM_NAME}" | cut -d"," -f3 | cut -d" " -f1); done + echo "$ADDR" 1>&3 +} + +discard_lxdvm() { + instance_name="$(lxc ls -f csv | sed ':a;N;$!ba;s/(docker0)\n/(docker0) /' | grep "$SPREAD_SYSTEM_ADDRESS " | cut -f1 -d",")" + lxc delete -f "$instance_name" +} + +allocate_ci() { + if [ -z "$CI" ]; then + echo "This backend is intended to be used only in CI systems." + exit 1 + fi + sudo sed -i 's/^\s*#\?\s*\(PermitRootLogin\|PasswordAuthentication\)\>.*/\1 yes/' /etc/ssh/sshd_config + if [ -d /etc/ssh/sshd_config.d ]; then echo -e 'PermitRootLogin yes\nPasswordAuthentication yes' | sudo tee /etc/ssh/sshd_config.d/00-spread.conf; fi + sudo systemctl daemon-reload + sudo systemctl restart ssh + + echo "root:${SPREAD_PASSWORD}" | sudo chpasswd || true + + # Print the instance address to stdout + echo localhost >&3 +} + +discard_ci() { + true +} + +allocate() { + exec 3>&1 + exec 1>&2 + + case "$1" in + lxd-vm) + allocate_lxdvm + ;; + ci) + allocate_ci + ;; + *) + echo "unsupported backend $1" 2>&1 + ;; + esac +} + +discard() { + case "$1" in + lxd-vm) + discard_lxdvm + ;; + ci) + discard_ci + ;; + *) + echo "unsupported backend $1" 2>&1 + ;; + esac +} + +set -e + +while getopts "" o; do + case "${o}" in + *) + usage + exit 1 + ;; + esac +done +shift $((OPTIND - 1)) + +CMD="$1" +PARM="$2" + +if [ -z "$CMD" ]; then + usage + exit 0 +fi + +case "$CMD" in +allocate) + allocate "$PARM" + ;; +discard) + discard "$PARM" + ;; +backend-prepare) + prepare + ;; +backend-restore) + restore + ;; +backend-prepare-each) + prepare_each + ;; +backend-restore-each) + restore_each + ;; +*) + echo "unknown command $CMD" >&2 + ;; +esac diff --git a/python/3.14-26.04/spread/general/app_hello-world/src/main.py b/python/3.14-26.04/spread/general/app_hello-world/src/main.py new file mode 100644 index 0000000..3b1848a --- /dev/null +++ b/python/3.14-26.04/spread/general/app_hello-world/src/main.py @@ -0,0 +1,9 @@ +#!/usr/bin/python3 + + +def main(): + print("Hello World") + + +if __name__ == "__main__": + main() diff --git a/python/3.14-26.04/spread/general/app_hello-world/task.yaml b/python/3.14-26.04/spread/general/app_hello-world/task.yaml new file mode 100644 index 0000000..4899023 --- /dev/null +++ b/python/3.14-26.04/spread/general/app_hello-world/task.yaml @@ -0,0 +1,7 @@ +summary: test python 3.14 with 26.04 base - hello world + +execute: | + docker run --rm -v "$PWD/src":/src:ro python:3.14-26.04 \ + exec -IT python3 /src/main.py | \ + grep "Hello World" + diff --git a/python/3.14-26.04/spread/general/app_https-get/src/main.py b/python/3.14-26.04/spread/general/app_https-get/src/main.py new file mode 100755 index 0000000..897525e --- /dev/null +++ b/python/3.14-26.04/spread/general/app_https-get/src/main.py @@ -0,0 +1,20 @@ +#!/usr/bin/python3 + +import http.client +import ssl + + +def main(): + url = "www.python.org" + try: + conn = http.client.HTTPSConnection(url, context=ssl.SSLContext()) + conn.request("GET", "/") + resp = conn.getresponse() + except Exception as e: + print(f"cannot connect to {url}: {e}") + exit(1) + print(resp.status, resp.reason) + + +if __name__ == "__main__": + main() diff --git a/python/3.14-26.04/spread/general/app_https-get/task.yaml b/python/3.14-26.04/spread/general/app_https-get/task.yaml new file mode 100644 index 0000000..93f73b2 --- /dev/null +++ b/python/3.14-26.04/spread/general/app_https-get/task.yaml @@ -0,0 +1,6 @@ +summary: test python 3.14 with 26.04 base - https get + +execute: | + docker run --rm -v "$PWD/src":/src:ro python:3.14-26.04 \ + exec -IT python3 /src/main.py | \ + grep "200 OK" diff --git a/python/3.8-20.04/spread/general/app_https-get/src/main.py b/python/3.8-20.04/spread/general/app_https-get/src/main.py index 60f0125..897525e 100755 --- a/python/3.8-20.04/spread/general/app_https-get/src/main.py +++ b/python/3.8-20.04/spread/general/app_https-get/src/main.py @@ -1,4 +1,4 @@ -#!/usr/bin/python3.8 +#!/usr/bin/python3 import http.client import ssl