Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add [snapcraft] version badge #9976

Merged
merged 17 commits into from
Mar 10, 2024
Merged

Add [snapcraft] version badge #9976

merged 17 commits into from
Mar 10, 2024

Conversation

jNullj
Copy link
Collaborator

@jNullj jNullj commented Feb 17, 2024

Example badge render:
image

Path: http://HOST/snapstore/v/:package

A few notes:

  1. Takes the latest value, might be a problem with mix of channels
  2. Currently does not use authentication - just proof of concept, I think we will have to add macaroon auth for this PR.
  3. I need to add tests.

Fixes #9103

@jNullj jNullj mentioned this pull request Feb 17, 2024
Copy link
Contributor

github-actions bot commented Feb 17, 2024

Messages
📖 ✨ Thanks for your contribution to Shields, @jNullj!

Generated by 🚫 dangerJS against 29dd792

@jNullj jNullj added service-badge Accepted and actionable changes, features, and bugs javascript Pull requests that update Javascript code labels Feb 17, 2024
@chris48s
Copy link
Member

I think we can get away without auth tbh. I think the macaroon auth is needed if you want to do stuff like publish snaps, for example. I'm not seeing anything that indicates the /info endpoint is rate limited without auth. Have you seen any docs indicating otherwise?

@jNullj jNullj marked this pull request as ready for review February 25, 2024 22:12
},
})

const version = parsedData['channel-map'][0].version
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry.. one more question

You said in the top post

Takes the latest value, might be a problem with mix of channels

Can you expand on that. Can we actually rely on array position 0 to be the latest version?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Snapstore allows devs to have more then one version channel, for example stable/beta/edge
They also seem to allow freedom when adding channel names.
Some snaps don't have stable channel, in those cases i found beta first or other channel.

I tested out a bunch of snaps and learned that:

  1. It appears that regardless of change date, the order of channels is fixed
  2. If stable exists i always saw it first in the list.

The docs indicate here that:

Some considerations about this structure:

if the channel doesn't have any revision released into it (e.g. beta is closed, so it just redirects to stable), it will be not present

branches will not be returned

items in the channel-map list will be ordered by (track, risk,
architecture); the track that is None will come always first in the list, using name 'latest'

fields inside the channel object could potentially grow to include other relevant info

if the snap doesn't have any released revision for the given request, channel-map will be present and empty

IMO its safe enough to assume the first channel has the most "mainstream" version, but i can not guarantee it won't fail.
I think we should not limit it to the stable/latest channel as some packages don't have it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK. Thanks for explaining that.

Although just picking the first one in the list means we will definitely put some value on the badge, I think it is maybe more important to focus on making sure it is clear and explainable what the value is.

I've had a bit of a look into this myself.

Lets take an example like chromium that has multiple channels and architectures

curl "https://api.snapcraft.io/v2/snaps/info/chromium" --header "Snap-Device-Series: 16"

Response
{
  "channel-map": [
    {
      "channel": {
        "architecture": "amd64",
        "name": "stable",
        "released-at": "2024-02-26T08:09:42.508032+00:00",
        "risk": "stable",
        "track": "latest"
      },
      "created-at": "2024-02-23T16:36:20.431472+00:00",
      "download": {
        "deltas": [],
        "sha3-384": "b12abc7c87caca9ef974d13e673ec25c44086376ec8c0480b9082e29779b1b451aa873cc7a0e31fefafc95b5979e9d46",
        "size": 168206336,
        "url": "https://api.snapcraft.io/api/v1/snaps/download/XKEcBqPM06H1Z7zGOdG5fbICuf8NWK5R_2764.snap"
      },
      "revision": 2764,
      "type": "app",
      "version": "122.0.6261.69"
    },
    {
      "channel": {
        "architecture": "arm64",
        "name": "stable",
        "released-at": "2024-02-26T08:09:43.882125+00:00",
        "risk": "stable",
        "track": "latest"
      },
      "created-at": "2024-02-24T05:26:00.972509+00:00",
      "download": {
        "deltas": [],
        "sha3-384": "30f878166c08cd54f9c313e356d8d9ed9b5f1f381de042cdbc454a68f5430b93071fdd39d09b96d60f8c313772e9c06e",
        "size": 168894464,
        "url": "https://api.snapcraft.io/api/v1/snaps/download/XKEcBqPM06H1Z7zGOdG5fbICuf8NWK5R_2765.snap"
      },
      "revision": 2765,
      "type": "app",
      "version": "122.0.6261.69"
    },
    {
      "channel": {
        "architecture": "armhf",
        "name": "stable",
        "released-at": "2024-01-19T23:45:32.994812+00:00",
        "risk": "stable",
        "track": "latest"
      },
      "created-at": "2024-01-19T10:39:30.179580+00:00",
      "download": {
        "deltas": [],
        "sha3-384": "63bcad9179720f327a337dac5ed94b22bb432632f5a1f5a25844c90bcd9c098f71568647e43b0d02b6bac75ae0eed7c8",
        "size": 144773120,
        "url": "https://api.snapcraft.io/api/v1/snaps/download/XKEcBqPM06H1Z7zGOdG5fbICuf8NWK5R_2737.snap"
      },
      "revision": 2737,
      "type": "app",
      "version": "120.0.6099.224"
    },
    {
      "channel": {
        "architecture": "i386",
        "name": "stable",
        "released-at": "2023-03-10T12:36:35.652771+00:00",
        "risk": "stable",
        "track": "latest"
      },
      "created-at": "2021-12-18T00:25:55.524093+00:00",
      "download": {
        "deltas": [],
        "sha3-384": "0635ccb12ca8aedcc95ff1505c0e313820140773567c344d79fcb5007492eea08c02ca19d727b9c7848fd4d2d564cf10",
        "size": 156057600,
        "url": "https://api.snapcraft.io/api/v1/snaps/download/XKEcBqPM06H1Z7zGOdG5fbICuf8NWK5R_1862.snap"
      },
      "revision": 1862,
      "type": "app",
      "version": "98.0.4758.9"
    },
    {
      "channel": {
        "architecture": "amd64",
        "name": "candidate",
        "released-at": "2024-02-28T15:51:03.536793+00:00",
        "risk": "candidate",
        "track": "latest"
      },
      "created-at": "2024-02-28T15:50:29.081075+00:00",
      "download": {
        "deltas": [],
        "sha3-384": "a4204c9d44bfe0d44a1e892c384d9e92700fac7af80b52dcfb61c8a6c8822c20d4e83b100d426cbc34891be250a0e1b7",
        "size": 168333312,
        "url": "https://api.snapcraft.io/api/v1/snaps/download/XKEcBqPM06H1Z7zGOdG5fbICuf8NWK5R_2768.snap"
      },
      "revision": 2768,
      "type": "app",
      "version": "122.0.6261.94"
    },
    {
      "channel": {
        "architecture": "arm64",
        "name": "candidate",
        "released-at": "2024-02-24T05:26:48.734893+00:00",
        "risk": "candidate",
        "track": "latest"
      },
      "created-at": "2024-02-24T05:26:00.972509+00:00",
      "download": {
        "deltas": [],
        "sha3-384": "30f878166c08cd54f9c313e356d8d9ed9b5f1f381de042cdbc454a68f5430b93071fdd39d09b96d60f8c313772e9c06e",
        "size": 168894464,
        "url": "https://api.snapcraft.io/api/v1/snaps/download/XKEcBqPM06H1Z7zGOdG5fbICuf8NWK5R_2765.snap"
      },
      "revision": 2765,
      "type": "app",
      "version": "122.0.6261.69"
    },
    {
      "channel": {
        "architecture": "armhf",
        "name": "candidate",
        "released-at": "2024-01-19T10:39:59.939262+00:00",
        "risk": "candidate",
        "track": "latest"
      },
      "created-at": "2024-01-19T10:39:30.179580+00:00",
      "download": {
        "deltas": [],
        "sha3-384": "63bcad9179720f327a337dac5ed94b22bb432632f5a1f5a25844c90bcd9c098f71568647e43b0d02b6bac75ae0eed7c8",
        "size": 144773120,
        "url": "https://api.snapcraft.io/api/v1/snaps/download/XKEcBqPM06H1Z7zGOdG5fbICuf8NWK5R_2737.snap"
      },
      "revision": 2737,
      "type": "app",
      "version": "120.0.6099.224"
    },
    {
      "channel": {
        "architecture": "i386",
        "name": "candidate",
        "released-at": "2023-03-08T11:02:34.371272+00:00",
        "risk": "candidate",
        "track": "latest"
      },
      "created-at": "2021-12-18T00:25:55.524093+00:00",
      "download": {
        "deltas": [],
        "sha3-384": "0635ccb12ca8aedcc95ff1505c0e313820140773567c344d79fcb5007492eea08c02ca19d727b9c7848fd4d2d564cf10",
        "size": 156057600,
        "url": "https://api.snapcraft.io/api/v1/snaps/download/XKEcBqPM06H1Z7zGOdG5fbICuf8NWK5R_1862.snap"
      },
      "revision": 1862,
      "type": "app",
      "version": "98.0.4758.9"
    },
    {
      "channel": {
        "architecture": "amd64",
        "name": "beta",
        "released-at": "2024-02-15T17:26:15.226443+00:00",
        "risk": "beta",
        "track": "latest"
      },
      "created-at": "2024-02-15T17:25:17.671423+00:00",
      "download": {
        "deltas": [],
        "sha3-384": "1c080882c47b2b884a8f036e6ea81af0707eccd228846e19dd3ebea89b958412d506236387ee2d64d851b6df0528bf6c",
        "size": 167301120,
        "url": "https://api.snapcraft.io/api/v1/snaps/download/XKEcBqPM06H1Z7zGOdG5fbICuf8NWK5R_2759.snap"
      },
      "revision": 2759,
      "type": "app",
      "version": "122.0.6261.39"
    },
    {
      "channel": {
        "architecture": "arm64",
        "name": "beta",
        "released-at": "2024-02-23T16:10:06.237907+00:00",
        "risk": "beta",
        "track": "latest"
      },
      "created-at": "2024-02-23T16:09:28.544255+00:00",
      "download": {
        "deltas": [],
        "sha3-384": "1557258f72d3a850ad0db062bcc1d2da31a1c041f241c827d7b284fd20a739b006b39853331f7e4d12cc24bda63994ee",
        "size": 168894464,
        "url": "https://api.snapcraft.io/api/v1/snaps/download/XKEcBqPM06H1Z7zGOdG5fbICuf8NWK5R_2763.snap"
      },
      "revision": 2763,
      "type": "app",
      "version": "122.0.6261.57"
    },
    {
      "channel": {
        "architecture": "armhf",
        "name": "beta",
        "released-at": "2023-12-06T08:46:56.603790+00:00",
        "risk": "beta",
        "track": "latest"
      },
      "created-at": "2023-12-06T08:46:25.832262+00:00",
      "download": {
        "deltas": [],
        "sha3-384": "f78e48a268263ff645028445bc71e6f5ea0a9945ac2abb67e07ee1d6014844d53a5e00128bc9ce9e66d3dbfb0ed373c4",
        "size": 144764928,
        "url": "https://api.snapcraft.io/api/v1/snaps/download/XKEcBqPM06H1Z7zGOdG5fbICuf8NWK5R_2711.snap"
      },
      "revision": 2711,
      "type": "app",
      "version": "120.0.6099.62"
    },
    {
      "channel": {
        "architecture": "i386",
        "name": "beta",
        "released-at": "2023-01-12T18:03:15.819763+00:00",
        "risk": "beta",
        "track": "latest"
      },
      "created-at": "2021-12-18T00:25:55.524093+00:00",
      "download": {
        "deltas": [],
        "sha3-384": "0635ccb12ca8aedcc95ff1505c0e313820140773567c344d79fcb5007492eea08c02ca19d727b9c7848fd4d2d564cf10",
        "size": 156057600,
        "url": "https://api.snapcraft.io/api/v1/snaps/download/XKEcBqPM06H1Z7zGOdG5fbICuf8NWK5R_1862.snap"
      },
      "revision": 1862,
      "type": "app",
      "version": "98.0.4758.9"
    },
    {
      "channel": {
        "architecture": "amd64",
        "name": "edge",
        "released-at": "2024-02-26T17:45:11.738171+00:00",
        "risk": "edge",
        "track": "latest"
      },
      "created-at": "2024-02-26T17:44:37.242988+00:00",
      "download": {
        "deltas": [],
        "sha3-384": "f6517f9cb7faf4997719cf2580ba49049686c0ab8f05d9442baa6d244e704fc4f797e2654fadda6cfe4147f79ba3f183",
        "size": 168693760,
        "url": "https://api.snapcraft.io/api/v1/snaps/download/XKEcBqPM06H1Z7zGOdG5fbICuf8NWK5R_2766.snap"
      },
      "revision": 2766,
      "type": "app",
      "version": "124.0.6315.2"
    },
    {
      "channel": {
        "architecture": "arm64",
        "name": "edge",
        "released-at": "2024-02-10T10:35:54.549248+00:00",
        "risk": "edge",
        "track": "latest"
      },
      "created-at": "2024-02-10T10:35:21.513150+00:00",
      "download": {
        "deltas": [],
        "sha3-384": "6ff1b84f222cf53425b19256d9d0e108d9835ab1b62f07c745d29c6e3aa27fbba2d3e906df2f6a19db782e6275a1a92a",
        "size": 168415232,
        "url": "https://api.snapcraft.io/api/v1/snaps/download/XKEcBqPM06H1Z7zGOdG5fbICuf8NWK5R_2756.snap"
      },
      "revision": 2756,
      "type": "app",
      "version": "123.0.6286.0"
    },
    {
      "channel": {
        "architecture": "armhf",
        "name": "edge",
        "released-at": "2023-11-18T04:22:03.340428+00:00",
        "risk": "edge",
        "track": "latest"
      },
      "created-at": "2023-11-18T04:21:38.521227+00:00",
      "download": {
        "deltas": [],
        "sha3-384": "dfac4a0a8da4b24c69e8ca8ffa17dbe3f195c2a5bb0675b72e1fb9b7a86021bdb195e43348e14b19e00b3609371d81df",
        "size": 145645568,
        "url": "https://api.snapcraft.io/api/v1/snaps/download/XKEcBqPM06H1Z7zGOdG5fbICuf8NWK5R_2700.snap"
      },
      "revision": 2700,
      "type": "app",
      "version": "121.0.6129.0"
    },
    {
      "channel": {
        "architecture": "i386",
        "name": "edge",
        "released-at": "2021-12-18T00:28:11.939534+00:00",
        "risk": "edge",
        "track": "latest"
      },
      "created-at": "2021-12-18T00:25:55.524093+00:00",
      "download": {
        "deltas": [],
        "sha3-384": "0635ccb12ca8aedcc95ff1505c0e313820140773567c344d79fcb5007492eea08c02ca19d727b9c7848fd4d2d564cf10",
        "size": 156057600,
        "url": "https://api.snapcraft.io/api/v1/snaps/download/XKEcBqPM06H1Z7zGOdG5fbICuf8NWK5R_1862.snap"
      },
      "revision": 1862,
      "type": "app",
      "version": "98.0.4758.9"
    }
  ],
  "default-track": null,
  "name": "chromium",
  "snap": {
    "license": "Apache-2.0 AND BSD-3-Clause AND LGPL-2.0 AND LGPL-2.1 AND MIT AND MS-PL AND (GPL-2.0+ OR LGPL-2.1+ OR MPL-1.1)",
    "name": "chromium",
    "prices": {},
    "publisher": {
      "display-name": "Canonical",
      "id": "canonical",
      "username": "canonical",
      "validation": "verified"
    },
    "snap-id": "XKEcBqPM06H1Z7zGOdG5fbICuf8NWK5R",
    "store-url": "https://snapcraft.io/chromium",
    "summary": "Chromium web browser, open-source version of Chrome",
    "title": "chromium"
  },
  "snap-id": "XKEcBqPM06H1Z7zGOdG5fbICuf8NWK5R"
}

the way this gets expressed through the command line is

$ snap info chromium
name:      chromium
summary:   Chromium web browser, open-source version of Chrome
publisher: Canonical✓
store-url: https://snapcraft.io/chromium
contact:   https://bugs.launchpad.net/ubuntu/+source/chromium-browser/+bugs?field.tag=snap
license:   unset
description: |
  An open-source browser project that aims to build a safer, faster, and more
  stable way for all Internet users to experience the web.
commands:
  - chromium.chromedriver
  - chromium
snap-id:      XKEcBqPM06H1Z7zGOdG5fbICuf8NWK5R
tracking:     latest/stable
refresh-date: 2 days ago, at 09:54 GMT
channels:
  latest/stable:    122.0.6261.69 2024-02-26 (2764) 168MB -
  latest/candidate: 122.0.6261.94 2024-02-28 (2768) 168MB -
  latest/beta:      122.0.6261.39 2024-02-15 (2759) 167MB -
  latest/edge:      124.0.6315.2  2024-02-26 (2766) 168MB -
installed:          122.0.6261.69            (2764) 168MB -

It seems to me like "channel" is really track and risk seperated by a slash.

Another decision we could make here is we say the route is /snapcraft/v/:package/:track/:risk e.g: /snapcraft/v/chromium/latest/stable or /snapcraft/v/chromium/latest/edge. Then it is totally clear and unambiguous what we are showing. I don't think it should be too onerous for the publisher of the package to supply those extra params. That's primarily who we're envisaging will be constructing the URL.

The other thing here is that for a given channel there may be multiple architectures in play. I think here we could take a cue from what we do with the docker version badge and take an optional ?arch query param, which defaults to amd64.

Some other snap packages you might find useful for testing are:

  • gtk-common-themes (available for lots of architectures)
  • thunderbird (multiple different channels all at different versions)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 on adding track and risk as path params, also adding arch as a query param seems natural.
I made those changes in the latest commits:
9ddcf16
2c4e2c4

The project convention is using namedLogo by default only for social badges.
This commit removes the default logo usage as this badge is not in the social category.
Keep 1 convention for the snapcraft badge, use only snapcraft and ditch snapstore.
Replace the costume render in SnapcraftVersion with renderVersionBadge
@jNullj jNullj changed the title Add [snapstore] version badge Add [snapcraft] version badge Feb 27, 2024
Added architecture query parameter: The snapcraft-version.service.js file now accepts an optional arch query parameter to specify the desired architecture for the Snap package. This defaults to amd64 if not provided.

If an unsupported architecture is specified in the query parameter, a NotFound error is thrown with a specific message indicating that the requested architecture is not found.

The snapcraft-version.tester.js file is updated to include a new test case that verifies the behavior when using the arch query parameter and also includes a test case for handling an invalid architecture.
Comment on lines 69 to 87
const channelMap = parsedData['channel-map']
let filteredChannelMap = channelMap.filter(
({ channel }) => channel.architecture === arch,
)
if (filteredChannelMap.length === 0) {
throw new NotFound({ prettyMessage: 'arch not found' })
}
filteredChannelMap = channelMap.filter(
({ channel }) => channel.track === track,
)
if (filteredChannelMap.length === 0) {
throw new NotFound({ prettyMessage: 'track not found' })
}
filteredChannelMap = channelMap.filter(
({ channel }) => channel.risk === risk,
)
if (filteredChannelMap.length === 0) {
throw new NotFound({ prettyMessage: 'risk not found' })
}
Copy link
Member

@chris48s chris48s Mar 3, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code looks wrong to me. These filters should be combined with AND logic, but we're filtering channelMap, assigning the result to filteredChannelMapand then essentially throwing away that result and overwriting filteredChannelMap. We're only really applying the last filter.

I think you really want to be doing

filteredChannelMap = filteredChannelMap.filter(...

for the second and third pass.

What would really help here would be if we split this filtering logic out into a transform() function and write a few unit tests (in a spec.js) to make sure this is working as expected.

You might look at something like

services/docker/docker-version.spec.js
or
services/crates/crates-base.spec.js

as a point of inspiration.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My bad, i copy-pasted without noticing i forgot to change to filteredChannelMap

Fixed filter logic to AND with b54f1e6
Moved the filter logic into transform with 75f8ac7
And most impotent, added testing with 29dd792
This test file is really called for.

The goal here was to filter by all conditions with logic and.
Before this fix the only the logic of the last filter is used.
Copy link
Contributor

🚀 Updated review app: https://pr-9976-badges-shields.fly.dev

@chris48s
Copy link
Member

This latest iteration is solid. Nice work 👍

@chris48s chris48s added this pull request to the merge queue Mar 10, 2024
Merged via the queue into badges:master with commit c6be456 Mar 10, 2024
22 checks passed
@jcs090218
Copy link

When will this be available? 🤔

@chris48s
Copy link
Member

I ran a deploy this evening
It is live now https://shields.io/badges/snapcraft-version

@jcs090218
Copy link

Thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
javascript Pull requests that update Javascript code service-badge Accepted and actionable changes, features, and bugs
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Snapcraft
3 participants