diff --git a/README.md b/README.md index aa95639..b528ad9 100644 --- a/README.md +++ b/README.md @@ -64,6 +64,7 @@ Available resource types: - `artifactapi_remote_terraform` - `artifactapi_remote_goproxy` - `artifactapi_remote_github_rpm` +- `artifactapi_remote_github_deb` #### Common Attributes @@ -95,11 +96,11 @@ Available resource types: | `ban_tags_enabled` | `false` | Enable tag banning | | `ban_tags` | | List of tags to ban | -#### Terraform / github_rpm-specific Attributes +#### Terraform / github_rpm / github_deb-specific Attributes | Attribute | Default | Description | |-------------------|---------|----------------------------------------------------------| -| `releases_remote` | `""` | Name of a backend remote that serves the download bytes. Terraform uses it for download URL rewriting; `github_rpm` 302-redirects `.rpm` downloads to it. | +| `releases_remote` | `""` | Name of a backend remote that serves the download bytes. Terraform uses it for download URL rewriting; `github_rpm`/`github_deb` 302-redirect `.rpm`/`.deb` downloads to it. | #### github_rpm-specific notes @@ -111,6 +112,19 @@ which release assets become packages (regex on asset filename). `password` may hold a token for private repos or higher API rate limits. See `examples/resources/artifactapi_remote_github_rpm/main.tf`. +#### github_deb-specific notes + +`artifactapi_remote_github_deb` is the Debian/apt analog of `github_rpm`: it +exposes a GitHub repo's release `.deb` assets as an `apt` repository, synthesizing +the apt index (`Packages`/`Release`) from release metadata without precaching +packages. Set `base_url` to the releases API root +(`https://api.github.com/repos/{owner}/{repo}`) and `releases_remote` to a +generic `github.com` remote that streams the `.deb` bytes; `.deb` downloads +302-redirect there. `patterns` filters which release assets become packages +(regex on asset filename). `password` may hold a token for private repos or +higher API rate limits. See +`examples/resources/artifactapi_remote_github_deb/main.tf`. + #### Example ```hcl diff --git a/examples/resources/artifactapi_remote_github_deb/main.tf b/examples/resources/artifactapi_remote_github_deb/main.tf new file mode 100644 index 0000000..5c30f21 --- /dev/null +++ b/examples/resources/artifactapi_remote_github_deb/main.tf @@ -0,0 +1,44 @@ +terraform { + required_providers { + artifactapi = { + source = "git.unkin.net/unkin/artifactapi" + version = "0.0.1" + } + } +} + +provider "artifactapi" { + endpoint = "https://artifactapi.example.com" +} + +# Backend generic remote that serves the actual .deb bytes from github.com. +# Its patterns must allowlist the target repo's release-download assets. +resource "artifactapi_remote_generic" "github" { + name = "github" + base_url = "https://github.com" + + patterns = [ + "goodtune/ghp/releases/download/.*\\.deb$", + ] +} + +# Metadata-only remote: scans goodtune/ghp releases for .deb assets and serves a +# synthesized apt repo. Packages are never precached; downloads 302-redirect to +# the "github" remote above. +resource "artifactapi_remote_github_deb" "goodtune_ghp" { + name = "goodtune-ghp" + base_url = "https://api.github.com/repos/goodtune/ghp" + description = "goodtune/ghp GitHub releases as an apt repo" + releases_remote = artifactapi_remote_generic.github.name + + mutable_ttl = 3600 + + # Optional: restrict which release assets become packages. + patterns = [ + ".*_amd64\\.deb$", + ".*_all\\.deb$", + ] + + # Optional: token for private repos / higher GitHub API rate limits. + # password = var.github_token +} diff --git a/internal/provider/provider.go b/internal/provider/provider.go index 545bb05..7c49034 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -69,6 +69,7 @@ func (p *ArtifactAPIProvider) Resources(_ context.Context) []func() resource.Res newRemoteResource("terraform"), newRemoteResource("goproxy"), newRemoteResource("github_rpm"), + newRemoteResource("github_deb"), NewVirtualResource, NewLocalTerraformResource, NewLocalPyPIResource, diff --git a/internal/provider/provider_test.go b/internal/provider/provider_test.go index d690f3f..c778d59 100644 --- a/internal/provider/provider_test.go +++ b/internal/provider/provider_test.go @@ -66,8 +66,8 @@ func TestProvider_Resources(t *testing.T) { p := &ArtifactAPIProvider{version: "1.0.0"} resources := p.Resources(context.Background()) - // 12 remote resource types + 1 virtual + local_terraform/pypi/rpm/deb/docker/generic = 19 - expectedCount := 19 + // 13 remote resource types + 1 virtual + local_terraform/pypi/rpm/deb/docker/generic = 20 + expectedCount := 20 if len(resources) != expectedCount { t.Fatalf("expected %d resources, got %d", expectedCount, len(resources)) } @@ -108,6 +108,7 @@ func TestProvider_Resources_ContainsExpectedTypes(t *testing.T) { "artifactapi_remote_terraform", "artifactapi_remote_goproxy", "artifactapi_remote_github_rpm", + "artifactapi_remote_github_deb", "artifactapi_virtual", "artifactapi_local_terraform", "artifactapi_local_pypi", diff --git a/internal/provider/resource_remote.go b/internal/provider/resource_remote.go index be6b445..0a43da7 100644 --- a/internal/provider/resource_remote.go +++ b/internal/provider/resource_remote.go @@ -68,6 +68,7 @@ func NewRemotePuppet() resource.Resource { return &remoteResource{packageType func NewRemoteTerraform() resource.Resource { return &remoteResource{packageType: "terraform"} } func NewRemoteGoProxy() resource.Resource { return &remoteResource{packageType: "goproxy"} } func NewRemoteGitHubRPM() resource.Resource { return &remoteResource{packageType: "github_rpm"} } +func NewRemoteGitHubDeb() resource.Resource { return &remoteResource{packageType: "github_deb"} } func (r *remoteResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { resp.TypeName = req.ProviderTypeName + "_remote_" + r.packageType @@ -147,7 +148,7 @@ func (r *remoteResource) Schema(_ context.Context, _ resource.SchemaRequest, res ElementType: types.StringType, }, "releases_remote": schema.StringAttribute{ - Description: "Name of a backend remote that serves the actual download bytes. Used by the terraform remote (download URL rewriting) and the github_rpm remote (302 redirect of .rpm downloads to a generic github.com remote).", + Description: "Name of a backend remote that serves the actual download bytes. Used by the terraform remote (download URL rewriting) and the github_rpm/github_deb remotes (302 redirect of .rpm/.deb downloads to a generic github.com remote).", Optional: true, Computed: true, Default: stringdefault.StaticString(""), }, "upstream_dial_timeout": schema.Int64Attribute{ diff --git a/internal/provider/resource_remote_test.go b/internal/provider/resource_remote_test.go index 0f77836..0515102 100644 --- a/internal/provider/resource_remote_test.go +++ b/internal/provider/resource_remote_test.go @@ -410,6 +410,7 @@ func TestRemoteResource_Metadata(t *testing.T) { {"pypi", "artifactapi_remote_pypi"}, {"npm", "artifactapi_remote_npm"}, {"github_rpm", "artifactapi_remote_github_rpm"}, + {"github_deb", "artifactapi_remote_github_deb"}, } for _, tt := range tests { @@ -453,6 +454,34 @@ func TestModelToAPI_GitHubRPM(t *testing.T) { } } +func TestModelToAPI_GitHubDeb(t *testing.T) { + ctx := context.Background() + r := &remoteResource{packageType: "github_deb"} + + model := remoteResourceModel{ + Name: types.StringValue("acme-tools"), + BaseURL: types.StringValue("https://api.github.com/repos/acme/tools"), + ReleasesRemote: types.StringValue("github"), + MutableTTL: types.Int64Value(3600), + Patterns: stringsToList(ctx, []string{`.*_amd64\.deb$`}), + } + + api := r.modelToAPI(ctx, model) + + if api.PackageType != "github_deb" { + t.Errorf("PackageType: expected github_deb, got %s", api.PackageType) + } + if api.BaseURL != "https://api.github.com/repos/acme/tools" { + t.Errorf("BaseURL: got %s", api.BaseURL) + } + if api.ReleasesRemote != "github" { + t.Errorf("ReleasesRemote: expected github, got %s", api.ReleasesRemote) + } + if len(api.Patterns) != 1 || api.Patterns[0] != `.*_amd64\.deb$` { + t.Errorf("Patterns: got %v", api.Patterns) + } +} + func TestRemoteResource_Schema(t *testing.T) { r := &remoteResource{packageType: "docker"} req := resource.SchemaRequest{} @@ -493,6 +522,7 @@ func TestNewRemoteResource_Constructors(t *testing.T) { {"terraform", func() resource.Resource { return NewRemoteTerraform() }, "terraform"}, {"goproxy", func() resource.Resource { return NewRemoteGoProxy() }, "goproxy"}, {"github_rpm", func() resource.Resource { return NewRemoteGitHubRPM() }, "github_rpm"}, + {"github_deb", func() resource.Resource { return NewRemoteGitHubDeb() }, "github_deb"}, } for _, tt := range tests {