Skip to content

Commit 59f2dd9

Browse files
authored
Add job to copy packages built in 2025 from robostack-staging to robostack-noetic and robostack-humble
1 parent 94323d2 commit 59f2dd9

File tree

2 files changed

+143
-0
lines changed

2 files changed

+143
-0
lines changed
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
name: Copy staging packages
2+
3+
on:
4+
workflow_dispatch:
5+
schedule:
6+
- cron: "42 * * * *"
7+
8+
jobs:
9+
copy-packages-noetic:
10+
runs-on: ubuntu-latest
11+
steps:
12+
- uses: actions/checkout@v4
13+
- uses: prefix-dev/setup-pixi@v0.8.1
14+
- env:
15+
ANACONDA_API_TOKEN: ${{ secrets.ROBOSTACK_NOETIC_ANACONDA_API_TOKEN }}
16+
run: |
17+
python copy-to-distro-specific-channels.py noetic 2025-01-01
18+
shell: pixi run bash -e {0}
19+
20+
copy-packages-humble:
21+
runs-on: ubuntu-latest
22+
steps:
23+
- uses: actions/checkout@v4
24+
- uses: prefix-dev/setup-pixi@v0.8.1
25+
- env:
26+
ANACONDA_API_TOKEN: ${{ secrets.ROBOSTACK_HUMBLE_ANACONDA_API_TOKEN }}
27+
run: |
28+
python copy-to-distro-specific-channels.py humble 2025-01-01
29+
shell: pixi run bash -e {0}

copy-to-distro-specific-channel.py

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
import requests
2+
import subprocess
3+
import argparse
4+
import datetime
5+
6+
# Configuration
7+
BASE_URL = "https://conda.anaconda.org"
8+
SOURCE_CHANNEL = "robostack-staging"
9+
PLATFORMS = ["linux-64", "linux-aarch64", "win-64", "osx-64", "osx-arm64", "noarch"]
10+
11+
def fetch_repodata(channel, platform):
12+
"""
13+
Fetch the repodata.json file from a given channel and platform.
14+
"""
15+
url = f"{BASE_URL}/{channel}/{platform}/repodata.json"
16+
response = requests.get(url)
17+
18+
if response.status_code == 200:
19+
return response.json()
20+
else:
21+
print(f"Error fetching repodata.json from {channel}/{platform}: {response.status_code}")
22+
return None
23+
24+
def upload_package(package_name_simple, package_name, version, build, platform, destination_channel):
25+
"""
26+
Upload a package to the specified Anaconda channel.
27+
"""
28+
try:
29+
# Construct the full package identifier with platform
30+
package_identifier = f"{SOURCE_CHANNEL}/{package_name_simple}/{version}/{platform}/{package_name}"
31+
32+
print(f"Uploading package: {package_name}, version: {version}, build: {build}, platform: {platform}")
33+
command_vec = ["anaconda", "copy", package_identifier, "--to-owner", destination_channel]
34+
subprocess.run(
35+
command_vec,
36+
check=True
37+
)
38+
print(f"Successfully uploaded: {package_name}, version: {version}, build: {build}, platform: {platform}")
39+
except subprocess.CalledProcessError as e:
40+
print(f"Failed to upload {package_name}, version {version}, build {build}, platform {platform}: {e}")
41+
42+
def main():
43+
# Parse command-line arguments
44+
parser = argparse.ArgumentParser(description="Upload packages from robostack-staging to robostack-<distroname>.")
45+
parser.add_argument(
46+
"distro",
47+
type=str,
48+
help="The distro upload (e.g., 'humble')"
49+
)
50+
parser.add_argument(
51+
"cutoff",
52+
type=str,
53+
help="Only package built after this cutoff date are uploaded. The cutoff date is a ISO 8601-formatted string."
54+
)
55+
args = parser.parse_args()
56+
distro = args.distro
57+
58+
destination_channel = f"robostack-{distro}"
59+
# debug
60+
destination_channel = f"robostack-{distro}-traversaro"
61+
62+
# Convert cutoff date to cutoff timestamps
63+
cutoff_timestamp = int(datetime.datetime.strptime(args.cutoff, "%Y-%m-%d").timestamp()*1000.0)
64+
65+
# Fetch repodata.json for each platform from source and destination channels
66+
source_repodata = {}
67+
destination_repodata = {}
68+
for platform in PLATFORMS:
69+
source_repodata[platform] = fetch_repodata(SOURCE_CHANNEL, platform) or {}
70+
destination_repodata[platform] = fetch_repodata(destination_channel, platform) or {}
71+
72+
# Process packages for each platform
73+
for platform in PLATFORMS:
74+
print(f"Processing platform: {platform}")
75+
76+
# Extract packages from source and destination
77+
source_packages = {}
78+
source_packages.update(source_repodata[platform].get("packages", {}))
79+
source_packages.update(source_repodata[platform].get("packages.conda", {}))
80+
destination_packages = {}
81+
destination_packages.update(destination_repodata[platform].get("packages", {}))
82+
destination_packages.update(destination_repodata[platform].get("packages.conda", {}))
83+
destination_keys = {
84+
(pkg_name, pkg_data["version"], pkg_data["build"])
85+
for pkg_name, pkg_data in destination_packages.items()
86+
}
87+
88+
# Filter packages that belong to the given distro
89+
# and are newer then the specified cutoff date
90+
prefix = 'ros-'+distro
91+
filtered_packages = {
92+
pkg_name: pkg_data
93+
for pkg_name, pkg_data in source_packages.items()
94+
# This should cover both packages that start with 'ros-<distro>'
95+
# '(ros|ros2)-<distro>-mutex' packages whose build string contains <distro>
96+
if (pkg_name.startswith(prefix) or (pkg_data["name"].endswith("distro-mutex") and distro in pkg_data["build"])) and (pkg_data["timestamp"] >= cutoff_timestamp)
97+
}
98+
99+
print(f"Found {len(filtered_packages)} packages in {SOURCE_CHANNEL}/{platform} that belong to distro {distro}")
100+
101+
# Upload packages that are not already in the destination channel
102+
for pkg_name, pkg_data in filtered_packages.items():
103+
package_name = pkg_name
104+
version = pkg_data["version"]
105+
build = pkg_data["build"]
106+
package_name_simple = pkg_data["name"]
107+
108+
if (package_name, version, build) not in destination_keys:
109+
upload_package(package_name_simple, package_name, version, build, platform, destination_channel)
110+
else:
111+
print(f"Package {package_name}, version {version}, build {build}, platform {platform} already exists in {destination_channel}. Skipping upload.")
112+
113+
if __name__ == "__main__":
114+
main()

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy