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/[email protected]
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/[email protected]
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)