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

GitLab kref #3661

Merged
merged 1 commit into from
Sep 9, 2022
Merged
Show file tree
Hide file tree
Changes from all 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
7 changes: 7 additions & 0 deletions Netkan/CKAN-netkan.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,12 @@
<Compile Include="Sources\Github\GithubReleaseAsset.cs" />
<Compile Include="Sources\Github\GithubRepo.cs" />
<Compile Include="Sources\Github\IGithubApi.cs" />
<Compile Include="Sources\Gitlab\GitlabApi.cs" />
<Compile Include="Sources\Gitlab\GitlabOptions.cs" />
<Compile Include="Sources\Gitlab\GitlabProject.cs" />
<Compile Include="Sources\Gitlab\GitlabRef.cs" />
<Compile Include="Sources\Gitlab\GitlabRelease.cs" />
<Compile Include="Sources\Gitlab\IGitlabApi.cs" />
<Compile Include="Sources\Jenkins\IJenkinsApi.cs" />
<Compile Include="Sources\Jenkins\JenkinsApi.cs" />
<Compile Include="Sources\Jenkins\JenkinsArtifact.cs" />
Expand All @@ -110,6 +116,7 @@
<Compile Include="Transformers\ForcedVTransformer.cs" />
<Compile Include="Transformers\GeneratedByTransformer.cs" />
<Compile Include="Transformers\GithubTransformer.cs" />
<Compile Include="Transformers\GitlabTransformer.cs" />
<Compile Include="Transformers\HttpTransformer.cs" />
<Compile Include="Transformers\InternalCkanTransformer.cs" />
<Compile Include="Transformers\ITransformer.cs" />
Expand Down
3 changes: 3 additions & 0 deletions Netkan/CmdLineOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ internal class CmdLineOptions
[Option("github-token", HelpText = "GitHub OAuth token for API access")]
public string GitHubToken { get; set; }

[Option("gitlab-token", HelpText = "GitLab OAuth token for API access")]
public string GitLabToken { get; set; }

[Option("net-useragent", DefaultValue = null, HelpText = "Set the default User-Agent string for HTTP requests")]
public string NetUserAgent { get; set; }

Expand Down
15 changes: 11 additions & 4 deletions Netkan/Processors/Inflator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ namespace CKAN.NetKAN.Processors
{
public class Inflator
{
public Inflator(string cacheDir, bool overwriteCache, string githubToken, bool prerelease)
public Inflator(string cacheDir, bool overwriteCache, string githubToken, string gitlabToken, bool prerelease)
{
log.Debug("Initializing inflator");
cache = FindCache(
Expand All @@ -28,7 +28,7 @@ public Inflator(string cacheDir, bool overwriteCache, string githubToken, bool p
IFileService fileService = new FileService(cache);
http = new CachingHttpService(cache, overwriteCache);
ckanValidator = new CkanValidator(http, moduleService);
transformer = new NetkanTransformer(http, fileService, moduleService, githubToken, prerelease, netkanValidator);
transformer = new NetkanTransformer(http, fileService, moduleService, githubToken, gitlabToken, prerelease, netkanValidator);
}

internal IEnumerable<Metadata> Inflate(string filename, Metadata netkan, TransformOptions opts)
Expand Down Expand Up @@ -56,8 +56,15 @@ internal IEnumerable<Metadata> Inflate(string filename, Metadata netkan, Transfo
}
catch (Exception)
{
// Purge anything we download for a failed indexing attempt from the cache to allow re-downloads
PurgeDownloads(http, cache);
try
{
// Purge anything we download for a failed indexing attempt from the cache to allow re-downloads
PurgeDownloads(http, cache);
}
catch
{
// Don't freak out if we can't delete
}
throw;
}
}
Expand Down
4 changes: 2 additions & 2 deletions Netkan/Processors/QueueHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,13 @@ namespace CKAN.NetKAN.Processors
{
public class QueueHandler
{
public QueueHandler(string inputQueueName, string outputQueueName, string cacheDir, bool overwriteCache, string githubToken, bool prerelease)
public QueueHandler(string inputQueueName, string outputQueueName, string cacheDir, bool overwriteCache, string githubToken, string gitlabToken, bool prerelease)
{
warningAppender = GetQueueLogAppender();
(LogManager.GetRepository() as Hierarchy)?.Root.AddAppender(warningAppender);

log.Debug("Initializing SQS queue handler");
inflator = new Inflator(cacheDir, overwriteCache, githubToken, prerelease);
inflator = new Inflator(cacheDir, overwriteCache, githubToken, gitlabToken, prerelease);

inputQueueURL = getQueueUrl(inputQueueName);
outputQueueURL = getQueueUrl(outputQueueName);
Expand Down
3 changes: 3 additions & 0 deletions Netkan/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ public static int Main(string[] args)
Options.CacheDir,
Options.OverwriteCache,
Options.GitHubToken,
Options.GitLabToken,
Options.PreRelease
);
inf.ValidateCkan(ckan);
Expand All @@ -72,6 +73,7 @@ public static int Main(string[] args)
Options.CacheDir,
Options.OverwriteCache,
Options.GitHubToken,
Options.GitLabToken,
Options.PreRelease
);
qh.Process();
Expand All @@ -89,6 +91,7 @@ public static int Main(string[] args)
Options.CacheDir,
Options.OverwriteCache,
Options.GitHubToken,
Options.GitLabToken,
Options.PreRelease
);
var ckans = inf.Inflate(
Expand Down
63 changes: 63 additions & 0 deletions Netkan/Sources/Gitlab/GitlabApi.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
using System;
using System.Linq;
using System.Collections.Generic;

using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

using CKAN.NetKAN.Services;

namespace CKAN.NetKAN.Sources.Gitlab
{
/// <summary>
/// Provides convenient access to the GitLab API
/// https://docs.gitlab.com/ee/api/
/// </summary>
internal sealed class GitlabApi : IGitlabApi
{
/// <summary>
/// Initialize the API object
/// </summary>
/// <param name="http">HTTP service for getting URLs</param>
/// <param name="token">GitLab API token</param>
public GitlabApi(IHttpService http, string token = null)
{
this.http = http;
this.token = token;
Comment on lines +25 to +26
Copy link
Member

Choose a reason for hiding this comment

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

Do we need to to setup a gitlab account for the indexer? Or will we get by until we reach their API limit?

Copy link
Member Author

Choose a reason for hiding this comment

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

I think we will want to create a GitLab token for this just for completeness/robustness, but I tested it without a token and it still worked. If I'm reading this table correctly, a token boosts the rate limiting from 500 to 2000 requests per minute:

https://docs.gitlab.com/ee/user/gitlab_com/index.html#gitlabcom-specific-rate-limits

}

/// <summary>
/// Retrieve info about a GitLab project from the API
/// </summary>
/// <param name="reference">Specification of which project to retrieve</param>
/// <returns>A project object</returns>
public GitlabProject GetProject(GitlabRef reference)
{
// https://docs.gitlab.com/ee/api/projects
return JsonConvert.DeserializeObject<GitlabProject>(
http.DownloadText(
new Uri(apiBase, $"{reference.Account}%2F{reference.Project}"),
token, null));
}

/// <summary>
/// Retrieve info about a GitLab project's releases from the API
/// </summary>
/// <param name="reference">Specification of which project's releases to retrieve</param>
/// <returns>Sequence of release objects from the API</returns>
public IEnumerable<GitlabRelease> GetAllReleases(GitlabRef reference)
{
// https://docs.gitlab.com/ee/api/releases/
return JArray.Parse(
http.DownloadText(
new Uri(apiBase, $"{reference.Account}%2F{reference.Project}/releases"),
token, null))
.Select(releaseJson => releaseJson.ToObject<GitlabRelease>());
}

private IHttpService http;
private string token;

private static readonly Uri apiBase = new Uri("https://gitlab.com/api/v4/projects/");
}
}
18 changes: 18 additions & 0 deletions Netkan/Sources/Gitlab/GitlabOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using Newtonsoft.Json;

namespace CKAN.NetKAN.Sources.Gitlab
{
/// <summary>
/// Represents the x_netkan_gitlab object from a netkan
/// </summary>
internal sealed class GitlabOptions
{
/// <summary>
/// True to use source ZIP for a release.
/// Note that this MUST be true because GitLab only provides source ZIPs!
/// If they add other assets in the future, this requirement can be relaxed.
/// </summary>
[JsonProperty("use_source_archive")]
public readonly bool UseSourceArchive = false;
Comment on lines +10 to +16
Copy link
Member

Choose a reason for hiding this comment

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

I might be misunderstanding the help text here, it says MUST be true and we're setting it to false?

Copy link
Member Author

@HebaruSan HebaruSan Sep 9, 2022

Choose a reason for hiding this comment

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

Yeah, it's kind-of supposed to be weird. The idea is to force all GitLab netkans to add the below metadata:

x_netkan_gitlab:
  use_source_archive: true

... until we can add the option of removing that. The = false default above only takes effect if the value isn't set in the netkan.

Let's look at this repo's releases to illustrate why:

https://gitlab.com/djdisodo/kspremote/-/releases

image

The app-debug.apk is the packaged file that the user is meant to download, but it's not provided by the API in a machine-readable form; it's just a Markdown-formatted link embedded in the description field:

https://gitlab.com/api/v4/projects/djdisodo%2Fkspremote/releases

[
  {
    "name": "release-1.1.0",
    "tag_name": "release-1.1.0",
    "description": "[app-debug.apk](/uploads/4ae81fe659c9d621ee64099522324c31/app-debug.apk)",
    "created_at": "2020-05-01T10:40:09.568Z",
    "released_at": "2020-05-01T10:40:09.567Z",
    "upcoming_release": false,
    "author": {
      "id": 2563867,
      "username": "djdisodo",
      "name": "lee jisoo",
      "state": "active",
      "avatar_url": "https://secure.gravatar.com/avatar/ed5d23ffb1070c0ed33cc8c6dead8d11?s=80&d=identicon",
      "web_url": "https://gitlab.com/djdisodo"
    },
    "commit": {
      "id": "a4a7d1112b2953dbcfa113fa9c68c8aebd3f751d",
      "short_id": "a4a7d111",
      "created_at": "2020-05-01T19:31:16.000+09:00",
      "parent_ids": [
        "66388dcd8b8881107b50e13545bfa6b4461b209a",
        "6359357ea2f3d18157bf04b18a63d6988d971616"
      ],
      "title": "Merge branch 'master' of https://gitlab.com/djdisodo/kspremote",
      "message": "Merge branch 'master' of https://gitlab.com/djdisodo/kspremote\n",
      "author_name": "djdisodo",
      "author_email": "[email protected]",
      "authored_date": "2020-05-01T19:31:16.000+09:00",
      "committer_name": "djdisodo",
      "committer_email": "[email protected]",
      "committed_date": "2020-05-01T19:31:16.000+09:00",
      "trailers": {},
      "web_url": "https://gitlab.com/djdisodo/kspremote/-/commit/a4a7d1112b2953dbcfa113fa9c68c8aebd3f751d"
    },
    "commit_path": "/djdisodo/kspremote/-/commit/a4a7d1112b2953dbcfa113fa9c68c8aebd3f751d",
    "tag_path": "/djdisodo/kspremote/-/tags/release-1.1.0",
    "assets": {
      "count": 4,
      "sources": [
        {
          "format": "zip",
          "url": "https://gitlab.com/djdisodo/kspremote/-/archive/release-1.1.0/kspremote-release-1.1.0.zip"
        },
        {
          "format": "tar.gz",
          "url": "https://gitlab.com/djdisodo/kspremote/-/archive/release-1.1.0/kspremote-release-1.1.0.tar.gz"
        },
        {
          "format": "tar.bz2",
          "url": "https://gitlab.com/djdisodo/kspremote/-/archive/release-1.1.0/kspremote-release-1.1.0.tar.bz2"
        },
        {
          "format": "tar",
          "url": "https://gitlab.com/djdisodo/kspremote/-/archive/release-1.1.0/kspremote-release-1.1.0.tar"
        }
      ],
      "links": []
    },
    "evidences": [],
    "_links": {
      "self": "https://gitlab.com/djdisodo/kspremote/-/releases/release-1.1.0"
    }
  },
  {
    "name": "release-beta4",
    "tag_name": "release-beta4",
    "description": "[app-debug.apk](/uploads/5db2c5ac589d55abd37c4dcd18942f6e/app-debug.apk)",
    "created_at": "2020-02-15T17:13:28.144Z",
    "released_at": "2020-02-15T17:13:28.134Z",
    "upcoming_release": false,
    "author": {
      "id": 2563867,
      "username": "djdisodo",
      "name": "lee jisoo",
      "state": "active",
      "avatar_url": "https://secure.gravatar.com/avatar/ed5d23ffb1070c0ed33cc8c6dead8d11?s=80&d=identicon",
      "web_url": "https://gitlab.com/djdisodo"
    },
    "commit": {
      "id": "6359357ea2f3d18157bf04b18a63d6988d971616",
      "short_id": "6359357e",
      "created_at": "2020-02-15T16:51:00.000+00:00",
      "parent_ids": [
        "af7fa415f0fd741c8ab5894dec7f8b9d09e5d746"
      ],
      "title": "Update README.md",
      "message": "Update README.md",
      "author_name": "lee jisoo",
      "author_email": "[email protected]",
      "authored_date": "2020-02-15T16:51:00.000+00:00",
      "committer_name": "lee jisoo",
      "committer_email": "[email protected]",
      "committed_date": "2020-02-15T16:51:00.000+00:00",
      "trailers": {},
      "web_url": "https://gitlab.com/djdisodo/kspremote/-/commit/6359357ea2f3d18157bf04b18a63d6988d971616"
    },
    "commit_path": "/djdisodo/kspremote/-/commit/6359357ea2f3d18157bf04b18a63d6988d971616",
    "tag_path": "/djdisodo/kspremote/-/tags/release-beta4",
    "assets": {
      "count": 4,
      "sources": [
        {
          "format": "zip",
          "url": "https://gitlab.com/djdisodo/kspremote/-/archive/release-beta4/kspremote-release-beta4.zip"
        },
        {
          "format": "tar.gz",
          "url": "https://gitlab.com/djdisodo/kspremote/-/archive/release-beta4/kspremote-release-beta4.tar.gz"
        },
        {
          "format": "tar.bz2",
          "url": "https://gitlab.com/djdisodo/kspremote/-/archive/release-beta4/kspremote-release-beta4.tar.bz2"
        },
        {
          "format": "tar",
          "url": "https://gitlab.com/djdisodo/kspremote/-/archive/release-beta4/kspremote-release-beta4.tar"
        }
      ],
      "links": []
    },
    "evidences": [],
    "_links": {
      "self": "https://gitlab.com/djdisodo/kspremote/-/releases/release-beta4"
    }
  }
]

If we want to index a mod from there using the API, we have 4 choices: source zip, source tar.gz, source tar.bz2, and source tar. That's why it says in various comments here that we can only support source archives for GitLab (and why we couldn't index that specific mod, even after this PR). The hope is that someday GitLab will improve their API to support non-source assets, and if/when that happens, we can remove this limitation.

}
}
25 changes: 25 additions & 0 deletions Netkan/Sources/Gitlab/GitlabProject.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using Newtonsoft.Json;

namespace CKAN.NetKAN.Sources.Gitlab
{
/// <summary>
/// Represents a project from the GitLab API
/// </summary>
public sealed class GitlabProject
{
[JsonProperty("name")]
public readonly string Name;

[JsonProperty("description")]
public readonly string Description;

[JsonProperty("web_url")]
public readonly string WebURL;

[JsonProperty("issues_enabled")]
public readonly bool IssuesEnabled;

[JsonProperty("readme_url")]
public readonly string ReadMeURL;
}
}
44 changes: 44 additions & 0 deletions Netkan/Sources/Gitlab/GitlabRef.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
using System.Text.RegularExpressions;

using CKAN.NetKAN.Model;

namespace CKAN.NetKAN.Sources.Gitlab
{
/// <summary>
/// Represents a GitLab $kref
/// </summary>
internal sealed class GitlabRef : RemoteRef
{
/// <summary>
/// Initialize the GitLab reference
/// </summary>
/// <param name="reference">The base $kref object from a netkan</param>
public GitlabRef(RemoteRef reference)
: base(reference)
{
var match = Pattern.Match(reference.Id);
if (match.Success)
{
Account = match.Groups["account"].Value;
Project = match.Groups["project"].Value;
}
else
{
throw new Kraken(string.Format(@"Could not parse reference: ""{0}""", reference));
}
}

/// <summary>
/// The first part of the "account/project" path from GitLab
/// </summary>
public readonly string Account;
/// <summary>
/// The second part of the "account/project" path from GitLab
/// </summary>
public readonly string Project;

private static readonly Regex Pattern = new Regex(
@"^(?<account>[^/]+)/(?<project>[^/]+)$",
RegexOptions.Compiled);
}
}
55 changes: 55 additions & 0 deletions Netkan/Sources/Gitlab/GitlabRelease.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
using System;
using System.Collections.Generic;

using Newtonsoft.Json;

namespace CKAN.NetKAN.Sources.Gitlab
{
/// <summary>
/// Represents a release from the GitLab API
/// </summary>
internal sealed class GitlabRelease
{
[JsonProperty("tag_name")]
public readonly string TagName;

[JsonProperty("author")]
public readonly GitlabReleaseAuthor Author = new GitlabReleaseAuthor();

[JsonProperty("released_at")]
public readonly DateTime ReleasedAt;

[JsonProperty("assets")]
public readonly GitlabReleaseAssets Assets = new GitlabReleaseAssets();
}

/// <summary>
/// Represents an author from the GitLab API
/// </summary>
internal sealed class GitlabReleaseAuthor
{
[JsonProperty("name")]
public readonly string Name;
}

/// <summary>
/// Represents an assets object from the GitLab API
/// </summary>
internal sealed class GitlabReleaseAssets
{
[JsonProperty("sources")]
public readonly List<GitlabReleaseAssetSource> Sources = new List<GitlabReleaseAssetSource>();
}

/// <summary>
/// Represents an assets source object from the GitLab API
/// </summary>
internal sealed class GitlabReleaseAssetSource
{
[JsonProperty("format")]
public readonly string Format;

[JsonProperty("url")]
public readonly string URL;
}
}
25 changes: 25 additions & 0 deletions Netkan/Sources/Gitlab/IGitlabApi.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using System.Collections.Generic;

namespace CKAN.NetKAN.Sources.Gitlab
{
/// <summary>
/// Interface for classes providing access to the GitLab API
/// Allows mocking up in tests
/// </summary>
internal interface IGitlabApi
{
/// <summary>
/// Retrieve info about a GitLab project from the API
/// </summary>
/// <param name="reference">Specification of which project to retrieve</param>
/// <returns>A project object</returns>
GitlabProject GetProject(GitlabRef reference);

/// <summary>
/// Retrieve info about a GitLab project's releases from the API
/// </summary>
/// <param name="reference">Specification of which project's releases to retrieve</param>
/// <returns>Sequence of release objects from the API</returns>
IEnumerable<GitlabRelease> GetAllReleases(GitlabRef reference);
}
}
Loading