Skip to content

Commit 5c88f8a

Browse files
Merge pull request #3437 from hujambo-dunia/instances-tiers
Add Tiers to Galaxy instances
2 parents 3df613f + c37dd2d commit 5c88f8a

8 files changed

Lines changed: 572 additions & 60 deletions

File tree

.github/CODEOWNERS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
content/community/coc/ @assuntad23 @beatrizserrano @frederikcoppens @hexylena @fubar2
22
content/news/ @natwhitaker
33
content/events/ @natwhitaker
4+
**/designations.csv @mschatz @bgruening @bebatut @nekrut @GarethPrice-Aus @blankenberg @hujambo-dunia @nakucher

content/usegalaxy/apply/index.md

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
---
2+
title: UseGalaxy Servers - Apply for UseGalaxy “dot-star” Membership
3+
---
4+
5+
## Who this is for
6+
7+
Operators of public Galaxy instances who want to join the **usegalaxy.\*** federation (the “dot-star” network).
8+
9+
## How to apply
10+
11+
1. **Open a new issue** in `galaxyproject/galaxy-hub` using the template **“usegalaxy.* Dot-Star Application.”**
12+
2. **Complete all required fields** and submit the issue for federation review.
13+
14+
## What you’ll provide (concise checklist)
15+
16+
- **Instance identity**
17+
- Region/Country
18+
- Public instance URL
19+
- Link to your public org/repos (infra configs, playbooks, containers, Helm charts)
20+
- **Infrastructure & reproducibility**
21+
- Visibility of infra/playbooks: Public / Partially public / Private (with explanation)
22+
- Notes on what’s public vs. private and any plans to open more
23+
- **User access & policies**
24+
- Access policy (registration open; anonymous jobs may be allowed but **not required**)
25+
- Terms of Service / Usage Policy URL
26+
- Data retention/deletion policy URL (incl. inactive accounts)
27+
- **Storage & reference data**
28+
- Default per-user storage quota (GB)
29+
- Short-term/scratch storage availability
30+
- CVMFS reference data availability/link
31+
- **Operations & reliability**
32+
- Approximate tool count
33+
- Public uptime/status page URL
34+
- Uptime over the last 90 days (%) + methodology/source
35+
- Upgrade policy & cadence (commitment to timely upgrades)
36+
- Cross-UseGalaxy testing participation (yes/planned/not yet)
37+
- Optional: Link to metrics dashboards (e.g., KUI)
38+
- **Community & stewardship**
39+
- Confirm adherence to the Galaxy Code of Conduct
40+
- Commit to participating in admin/community channels (SIGs, meetings, Matrix/mailing lists)
41+
- Commit to contributing back issues/PRs/knowledge where feasible
42+
- **People & proof**
43+
- Operations team, on-call/incident process, and support channels
44+
- Evidence & references (links to PRs, monitoring panels, docs, prior discussions)
45+
- Any open questions for reviewers
46+
47+
## What happens next
48+
49+
- Federation maintainers review your application issue, may ask follow-ups, and confirm alignment with expectations (e.g., uptime, upgrade cadence, cross-testing).
50+
- Upon acceptance, your site is listed with the UseGalaxy servers and aligned with federation practices (e.g., CVMFS reference data).

content/usegalaxy/designations.csv

Lines changed: 173 additions & 0 deletions
Large diffs are not rendered by default.

content/usegalaxy/index.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ We want to see servers meeting several loose requirements to be considered part
5050
- *Functional*: The site provides >90% uptime as monitored by the [status page](https://status.galaxyproject.org/), and participates in automated cross-usegalaxy testing initiatives.
5151
- *Conduct*: Adhere to the [Galaxy Project Code of Conduct](https://galaxyproject.org/community/coc/).
5252

53+
For more information, please read more about the [application process here](apply/).
54+
5355
We're excited to see new services join us!
5456

5557
## Incubating Members

gridsome.server.js

Lines changed: 114 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,27 @@ class nodeModifier {
332332
}
333333
return node;
334334
},
335+
Platform: function (node) {
336+
// Process each platform URL to add designation data
337+
if (node.platforms && Array.isArray(node.platforms)) {
338+
for (const platform of node.platforms) {
339+
platform.designation = null;
340+
341+
if (platform.platform_url) {
342+
const url = normalizeUrl(platform.platform_url);
343+
const designation = DESIGNATIONS_MAP.get(url);
344+
if (designation) {
345+
platform.designation = designation;
346+
console.log(
347+
`Found designation for ${platform.platform_url}: ${designation.designation} (Tier ${designation.tier})`,
348+
);
349+
}
350+
}
351+
}
352+
}
353+
354+
return node;
355+
},
335356
};
336357
}
337358

@@ -383,6 +404,71 @@ function parseNavbarItem(rawItem, pathPrefix) {
383404
return item;
384405
}
385406

407+
// Helper function to normalize URLs for consistent matching
408+
function normalizeUrl(url) {
409+
if (!url) return null;
410+
return url
411+
.replace(/^https?:\/\//, "") // Remove protocol
412+
.replace(/\/$/, "") // Remove trailing slash
413+
.replace(/:\d+$/, "") // Remove port numbers
414+
.toLowerCase(); // Normalize case
415+
}
416+
417+
function loadDesignationsData() {
418+
const designationsPath = path.join(__dirname, "content/usegalaxy/designations.csv");
419+
if (!fs.existsSync(designationsPath)) {
420+
console.warn("Designations CSV file not found:", designationsPath);
421+
return new Map();
422+
}
423+
424+
const csvContent = fs.readFileSync(designationsPath, "utf8");
425+
const lines = csvContent.split("\n").filter((line) => line.trim());
426+
const headers = lines[0].split(",").map((h) => h.trim());
427+
const designationsMap = new Map();
428+
429+
for (let i = 1; i < lines.length; i++) {
430+
const line = lines[i];
431+
if (!line.trim()) continue;
432+
433+
// Parse CSV line, handling quoted values
434+
const values = [];
435+
let current = "";
436+
let inQuotes = false;
437+
438+
for (let j = 0; j < line.length; j++) {
439+
const char = line[j];
440+
if (char === '"') {
441+
inQuotes = !inQuotes;
442+
} else if (char === "," && !inQuotes) {
443+
values.push(current.trim());
444+
current = "";
445+
} else {
446+
current += char;
447+
}
448+
}
449+
values.push(current.trim());
450+
451+
if (values.length >= headers.length) {
452+
const designation = {};
453+
headers.forEach((header, index) => {
454+
designation[header.toLowerCase()] = values[index] || "";
455+
});
456+
457+
// Show all designations with URLs
458+
if (designation.url) {
459+
const normalizedUrl = normalizeUrl(designation.url);
460+
if (normalizedUrl) {
461+
designationsMap.set(normalizedUrl, designation);
462+
}
463+
}
464+
}
465+
}
466+
467+
return designationsMap;
468+
}
469+
470+
const DESIGNATIONS_MAP = loadDesignationsData();
471+
386472
module.exports = function (api) {
387473
api.loadSource((actions) => {
388474
// Using the Data Store API: https://gridsome.org/docs/data-store-api/
@@ -426,7 +512,7 @@ module.exports = function (api) {
426512
};
427513
}
428514
actions.addSchemaResolvers(schemas);
429-
actions.addSchemaTypes(
515+
actions.addSchemaTypes([
430516
actions.schema.createObjectType({
431517
name: "Dataset",
432518
interfaces: ["Node"],
@@ -436,7 +522,30 @@ module.exports = function (api) {
436522
main_subsite: "String",
437523
},
438524
}),
439-
);
525+
actions.schema.createObjectType({
526+
name: "Platform",
527+
interfaces: ["Node"],
528+
extensions: { infer: true },
529+
fields: {
530+
designation: {
531+
type: "PlatformDesignation",
532+
resolve: (obj) => obj.designation,
533+
},
534+
},
535+
}),
536+
actions.schema.createObjectType({
537+
name: "PlatformDesignation",
538+
fields: {
539+
operative: "String",
540+
tier: "Int",
541+
url: "String",
542+
city: "String",
543+
region: "String",
544+
description: "String",
545+
notes: "String",
546+
},
547+
}),
548+
]);
440549
});
441550

442551
// Populate the derived fields.
@@ -532,6 +641,9 @@ module.exports = function (api) {
532641
platform_text
533642
platform_location
534643
platform_purview
644+
designation {
645+
tier
646+
}
535647
}
536648
}
537649
}

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
},
2828
"dependencies": {
2929
"@babel/core": "^7.16.5",
30-
"@fortawesome/fontawesome-free": "^5.15.4",
30+
"@fortawesome/fontawesome-free": "^6.7.2",
3131
"@gridsome/plugin-sitemap": "^0.4.0",
3232
"@gridsome/source-filesystem": "^0.6.2",
3333
"@gridsome/transformer-remark": "^0.6.4",

src/composables/useTableSorting.js

Lines changed: 31 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ export function useTableSorting() {
2828
platform: platformSortHandler,
2929
title: titleSortHandler,
3030
summary: textSortHandler,
31-
// tier: tierSortHandler,
31+
tier: tierSortHandler,
3232
region: regionSortHandler,
3333
tools_count: toolsCountSortHandler,
3434
references_count: referencesCountSortHandler,
@@ -83,17 +83,17 @@ export function useTableSorting() {
8383
* @param {Object} bRow - Second row object
8484
* @returns {number} - Sort result (-1, 0, 1)
8585
*/
86-
// const tierSortHandler = (aRow, bRow) => {
87-
// // TODO refactor into another method
88-
// const a = getTierValue(aRow);
89-
// const b = getTierValue(bRow);
86+
const tierSortHandler = (aRow, bRow) => {
87+
// TODO refactor into another method
88+
const a = getTierValue(aRow);
89+
const b = getTierValue(bRow);
9090

91-
// if (a === null && b === null) return 0;
92-
// if (a === null) return 1;
93-
// if (b === null) return -1;
91+
if (a === null && b === null) return 0;
92+
if (a === null) return 1;
93+
if (b === null) return -1;
9494

95-
// return a - b;
96-
// };
95+
return a - b;
96+
};
9797

9898
/**
9999
* Sort handler for region columns (handles string region values)
@@ -156,17 +156,25 @@ export function useTableSorting() {
156156
* @param {Object} row - Row object
157157
* @returns {number|null} - Tier value for sorting
158158
*/
159-
// const getTierValue = (row) => {
160-
// if (row.designation && Array.isArray(row.designation) && row.designation.length > 0) {
161-
// const tier = parseInt(row.designation[0].tier, 10);
162-
// return isNaN(tier) ? null : tier;
163-
// }
164-
// if (row.designation && row.designation.tier) {
165-
// const tier = parseInt(row.designation.tier, 10);
166-
// return isNaN(tier) ? null : tier;
167-
// }
168-
// return null;
169-
// };
159+
const getTierValue = (row, platform_group = null) => {
160+
if (!row.platforms || !Array.isArray(row.platforms)) {
161+
return null;
162+
}
163+
164+
// Find the platform with the matching group that has designation data
165+
for (const platform of row.platforms) {
166+
if (platform_group && platform.platform_group !== platform_group) {
167+
continue;
168+
}
169+
170+
if (platform.designation && platform.designation.tier) {
171+
const tier = parseInt(platform.designation.tier, 10);
172+
return isNaN(tier) ? null : tier;
173+
}
174+
}
175+
176+
return null;
177+
};
170178

171179
/**
172180
* Get the region value for sorting (returns string value or null)
@@ -281,7 +289,7 @@ export function useTableSorting() {
281289
platformSortHandler,
282290
titleSortHandler,
283291
textSortHandler,
284-
// tierSortHandler,
292+
tierSortHandler,
285293
regionSortHandler,
286294
toolsCountSortHandler,
287295
referencesCountSortHandler,
@@ -290,7 +298,7 @@ export function useTableSorting() {
290298
createSortableFields,
291299

292300
getPlatformDisplayValue,
293-
// getTierValue,
301+
getTierValue,
294302
getRegionValue,
295303
compareStrings,
296304
getSortState,

0 commit comments

Comments
 (0)