Skip to content

Commit da28595

Browse files
Merge pull request #68 from traversaro/copystaging
Add job to copy packages built in 2025 from robostack-staging to robostack-noetic and robostack-humble
2 parents 3f5f9a6 + 0138a54 commit da28595

File tree

4 files changed

+1076
-49
lines changed

4 files changed

+1076
-49
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: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
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 a string with format YYYY-MM-DD."
54+
)
55+
args = parser.parse_args()
56+
distro = args.distro
57+
58+
destination_channel = f"robostack-{distro}"
59+
60+
# Convert cutoff date to cutoff timestamps
61+
cutoff_timestamp = int(datetime.datetime.strptime(args.cutoff, "%Y-%m-%d").timestamp()*1000.0)
62+
63+
# Fetch repodata.json for each platform from source and destination channels
64+
source_repodata = {}
65+
destination_repodata = {}
66+
for platform in PLATFORMS:
67+
source_repodata[platform] = fetch_repodata(SOURCE_CHANNEL, platform) or {}
68+
destination_repodata[platform] = fetch_repodata(destination_channel, platform) or {}
69+
70+
# Process packages for each platform
71+
for platform in PLATFORMS:
72+
print(f"Processing platform: {platform}")
73+
74+
# Extract packages from source and destination
75+
source_packages = {}
76+
source_packages.update(source_repodata[platform].get("packages", {}))
77+
source_packages.update(source_repodata[platform].get("packages.conda", {}))
78+
destination_packages = {}
79+
destination_packages.update(destination_repodata[platform].get("packages", {}))
80+
destination_packages.update(destination_repodata[platform].get("packages.conda", {}))
81+
destination_keys = {
82+
(pkg_name, pkg_data["version"], pkg_data["build"])
83+
for pkg_name, pkg_data in destination_packages.items()
84+
}
85+
86+
# Filter packages that belong to the given distro
87+
# and are newer then the specified cutoff date
88+
prefix = 'ros-'+distro
89+
filtered_packages = {
90+
pkg_name: pkg_data
91+
for pkg_name, pkg_data in source_packages.items()
92+
# This should cover both packages that start with 'ros-<distro>'
93+
# '(ros|ros2)-<distro>-mutex' packages whose build string contains <distro>
94+
if (pkg_name.startswith(prefix) or (pkg_data["name"].endswith("distro-mutex") and distro in pkg_data["build"])) and (pkg_data["timestamp"] >= cutoff_timestamp)
95+
}
96+
97+
print(f"Found {len(filtered_packages)} packages in {SOURCE_CHANNEL}/{platform} that belong to distro {distro}")
98+
99+
# Upload packages that are not already in the destination channel
100+
for pkg_name, pkg_data in filtered_packages.items():
101+
package_name = pkg_name
102+
version = pkg_data["version"]
103+
build = pkg_data["build"]
104+
package_name_simple = pkg_data["name"]
105+
106+
if (package_name, version, build) not in destination_keys:
107+
upload_package(package_name_simple, package_name, version, build, platform, destination_channel)
108+
else:
109+
print(f"Package {package_name}, version {version}, build {build}, platform {platform} already exists in {destination_channel}. Skipping upload.")
110+
111+
if __name__ == "__main__":
112+
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