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
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions services/snapstore/snapstore-version.service.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import Joi from 'joi'
import { BaseJsonService, pathParams } from '../index.js'

const versionSchema = Joi.object({
'channel-map': Joi.array().items(
Joi.object({
version: Joi.string().required(),
}),
),
chris48s marked this conversation as resolved.
Show resolved Hide resolved
}).required()

export default class SnapstoreVersion extends BaseJsonService {
static category = 'version'

static route = {
base: 'snapstore/v',
pattern: ':package',
}

static defaultBadgeData = { label: 'snapcraft', namedLogo: 'snapcraft' }
chris48s marked this conversation as resolved.
Show resolved Hide resolved

static openApi = {
'/snapstore/v/{package}': {
get: {
summary: 'Snapcraft version',
chris48s marked this conversation as resolved.
Show resolved Hide resolved
parameters: pathParams({
name: 'package',
example: 'vim-editor',
}),
},
},
}

static render({ version }) {
return { message: version, color: 'blue', namedLogo: 'snapcraft' }
}

async handle({ package: packageName }) {
const parsedData = await this._requestJson({
schema: versionSchema,
options: {
headers: { 'Snap-Device-Series': 16 },
},
url: `https://api.snapcraft.io/v2/snaps/info/${packageName}`,
httpErrors: {
404: 'package not found',
},
})

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


return this.constructor.render({ version })
chris48s marked this conversation as resolved.
Show resolved Hide resolved
}
}
15 changes: 15 additions & 0 deletions services/snapstore/snapstore-version.tester.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { isSemver } from '../test-validators.js'
import { createServiceTester } from '../tester.js'
export const t = await createServiceTester()

t.create('Snapcraft Version for redis').get('/redis.json').expectBadge({
label: 'snapcraft',
message: isSemver,
})

t.create('Snapcraft Version for redis (invalid package)')
.get('/this_package_doesnt_exist.json')
.expectBadge({
label: 'snapcraft',
message: 'package not found',
})
Loading