diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml
index b38e497bf..8da4335af 100644
--- a/.github/workflows/golangci-lint.yml
+++ b/.github/workflows/golangci-lint.yml
@@ -35,7 +35,7 @@ jobs:
- name: Install and initialize swagger
run: |
go install github.com/swaggo/swag/cmd/swag@latest
- swag init
+ swag init -q --markdownFiles docs
shell: sh
- name: golangci-lint
diff --git a/.github/workflows/scripts/upload-artifacts.sh b/.github/workflows/scripts/upload-artifacts.sh
index 181ff1e03..e41697df4 100755
--- a/.github/workflows/scripts/upload-artifacts.sh
+++ b/.github/workflows/scripts/upload-artifacts.sh
@@ -82,6 +82,7 @@ update_publish() {
_state=`echo $jsonret | jq .State`
if [ "$_state" = "2" ]; then
_success=1
+ curl -fsS -X DELETE -u $aptly_user:$aptly_password ${aptly_api}/api/tasks/$_task_id
break
fi
if [ "$_state" = "3" ]; then
diff --git a/.gitignore b/.gitignore
index 6b1b20816..495b35f50 100644
--- a/.gitignore
+++ b/.gitignore
@@ -67,4 +67,7 @@ debian/aptly-dbg.debhelper.log
debian/aptly-dbg.substvars
debian/aptly-dbg/
-docs/
+docs/docs.go
+docs/swagger.json
+docs/swagger.yaml
+docs/swagger.conf
diff --git a/Makefile b/Makefile
index 6e1411096..99c628408 100644
--- a/Makefile
+++ b/Makefile
@@ -3,70 +3,80 @@ VERSION=$(shell make -s version)
PYTHON?=python3
TESTS?=
BINPATH?=$(GOPATH)/bin
-RUN_LONG_TESTS?=yes
GOLANGCI_LINT_VERSION=v1.54.1 # version supporting go 1.19
COVERAGE_DIR?=$(shell mktemp -d)
GOOS=$(shell go env GOHOSTOS)
GOARCH=$(shell go env GOHOSTARCH)
-RELEASE=no
+
# Uncomment to update test outputs
# CAPTURE := "--capture"
-# Self-documenting Makefile
-# https://marmelab.com/blog/2016/02/29/auto-documented-makefile.html
help: ## Print this help
@grep -E '^[a-zA-Z][a-zA-Z0-9_-]*:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
prepare: ## Install go module dependencies
- # set version
- @make version > VERSION
- # download modules
- go mod download
- # install and initialize swagger
- go install github.com/swaggo/swag/cmd/swag@latest
- PATH=$(BINPATH)/:$(PATH) swag init -q
+ # Prepare go modules
go mod verify
go mod tidy -v
+ # Generate VERSION file
go generate
-install:
- @echo "\e[33m\e[1mBuilding aptly ...\e[0m"
- go generate
- @out=`mktemp`; if ! go install -v > $$out 2>&1; then cat $$out; rm -f $$out; echo "\nBuild failed\n"; exit 1; else rm -f $$out; fi
+releasetype: # Print release type: ci (on any branch/commit), release (on a tag)
+ @reltype=ci ; \
+ gitbranch=`git rev-parse --abbrev-ref HEAD` ; \
+ if [ "$$gitbranch" = "HEAD" ] && [ "$$FORCE_CI" != "true" ]; then \
+ gittag=`git describe --tags --exact-match 2>/dev/null` ;\
+ if echo "$$gittag" | grep -q '^v[0-9]'; then \
+ reltype=release ; \
+ fi ; \
+ fi ; \
+ echo $$reltype
-system-test: install ## Run system tests in github CI
-ifeq ($(RUN_LONG_TESTS), yes)
- go generate
- # install etcd
+version: ## Print aptly version
+ @ci="" ; \
+ if [ "`make -s releasetype`" = "ci" ]; then \
+ ci=`TZ=UTC git show -s --format='+%cd.%h' --date=format-local:'%Y%m%d%H%M%S'`; \
+ fi ; \
+ if which dpkg-parsechangelog > /dev/null 2>&1; then \
+ echo `dpkg-parsechangelog -S Version`$$ci; \
+ else \
+ echo `grep ^aptly -m1 debian/changelog | sed 's/.*(\([^)]\+\)).*/\1/'`$$ci ; \
+ fi
+
+swagger-install:
+ # Install swag
+ @test -f $(BINPATH)/swag || GOOS=linux GOARCH=amd64 go install github.com/swaggo/swag/cmd/swag@latest
+ # Generate swagger.conf
+ cp docs/swagger.conf.tpl docs/swagger.conf
+ echo "// @version $(VERSION)" >> docs/swagger.conf
+
+swagger: swagger-install
+ # Generate swagger docs
+ @PATH=$(BINPATH)/:$(PATH) swag init --markdownFiles docs --generalInfo docs/swagger.conf
+
+etcd-install:
+ # Install etcd
test -d /srv/etcd || system/t13_etcd/install-etcd.sh
- system/t13_etcd/start-etcd.sh &
- # build coverage binary
- go test -v -coverpkg="./..." -c -tags testruncli
- kill `cat /tmp/etcd.pid`
- if [ ! -e ~/aptly-fixture-db ]; then git clone https://github.com/aptly-dev/aptly-fixture-db.git ~/aptly-fixture-db/; fi
- if [ ! -e ~/aptly-fixture-pool ]; then git clone https://github.com/aptly-dev/aptly-fixture-pool.git ~/aptly-fixture-pool/; fi
- cd /home/runner; curl -O http://repo.aptly.info/system-tests/etcd.db.xz; xz -d etcd.db.xz
- PATH=$(BINPATH)/:$(PATH) && APTLY_VERSION=$(VERSION) FORCE_COLOR=1 $(PYTHON) system/run.py --long $(TESTS) --coverage-dir $(COVERAGE_DIR) $(CAPTURE)
-endif
+flake8: ## run flake8 on system test python files
+ flake8 system/
+
+lint:
+ # Install golangci-lint
+ go install github.com/golangci/golangci-lint/cmd/golangci-lint@$(GOLANGCI_LINT_VERSION)
+ # Running lint
+ @PATH=$(BINPATH)/:$(PATH) golangci-lint run
-docker-test: ## Run system tests
- @echo "\e[33m\e[1mBuilding aptly.test ...\e[0m"
- @rm -f aptly.test
+
+build: prepare swagger ## Build aptly
+ go build -o build/aptly
+
+install:
+ @echo "\e[33m\e[1mBuilding aptly ...\e[0m"
go generate
- # install and initialize swagger
- test -f $(BINPATH)/swag || go install github.com/swaggo/swag/cmd/swag@latest
- PATH=$(BINPATH)/:$(PATH) swag init -q
- # build coverage binary
- go test -v -coverpkg="./..." -c -tags testruncli
- @echo "\e[33m\e[1mRunning python tests ...\e[0m"
- @test -e aws.creds && . ./aws.creds; \
- export PATH=$(BINPATH)/:$(PATH); \
- export APTLY_VERSION=$(VERSION); \
- $(PYTHON) system/run.py --long $(TESTS) --coverage-dir $(COVERAGE_DIR) $(CAPTURE) $(TEST)
-
-test: prepare ## Run unit tests
- @test -d /srv/etcd || system/t13_etcd/install-etcd.sh
+ @out=`mktemp`; if ! go install -v > $$out 2>&1; then cat $$out; rm -f $$out; echo "\nBuild failed\n"; exit 1; else rm -f $$out; fi
+
+test: prepare swagger etcd-install ## Run unit tests
@echo "\e[33m\e[1mStarting etcd ...\e[0m"
@mkdir -p /tmp/etcd-data; system/t13_etcd/start-etcd.sh > /tmp/etcd-data/etcd.log 2>&1 &
@echo "\e[33m\e[1mRunning go test ...\e[0m"
@@ -76,62 +86,28 @@ test: prepare ## Run unit tests
@rm -f /tmp/etcd-data/etcd.log
@ret=`cat .unit-test.ret`; if [ "$$ret" = "0" ]; then echo "\n\e[32m\e[1mUnit Tests SUCCESSFUL\e[0m"; else echo "\n\e[31m\e[1mUnit Tests FAILED\e[0m"; fi; rm -f .unit-test.ret; exit $$ret
+system-test: prepare swagger etcd-install ## Run system tests
+ # build coverage binary
+ go test -v -coverpkg="./..." -c -tags testruncli
+ # Download fixture-db, fixture-pool, etcd.db
+ if [ ! -e ~/aptly-fixture-db ]; then git clone https://github.com/aptly-dev/aptly-fixture-db.git ~/aptly-fixture-db/; fi
+ if [ ! -e ~/aptly-fixture-pool ]; then git clone https://github.com/aptly-dev/aptly-fixture-pool.git ~/aptly-fixture-pool/; fi
+ test -f ~/etcd.db || (curl -o ~/etcd.db.xz http://repo.aptly.info/system-tests/etcd.db.xz && xz -d ~/etcd.db.xz)
+ # Run system tests
+ PATH=$(BINPATH)/:$(PATH) && FORCE_COLOR=1 $(PYTHON) system/run.py --long $(TESTS) --coverage-dir $(COVERAGE_DIR) $(CAPTURE) $(TEST)
+
bench:
@echo "\e[33m\e[1mRunning benchmark ...\e[0m"
go test -v ./deb -run=nothing -bench=. -benchmem
-mem.png: mem.dat mem.gp
- gnuplot mem.gp
- open mem.png
-
-man: ## Create man pages
- make -C man
-
-version: ## Print aptly version
- @ci="" ; \
- if [ "`make -s releasetype`" = "ci" ]; then \
- ci=`TZ=UTC git show -s --format='+%cd.%h' --date=format-local:'%Y%m%d%H%M%S'`; \
- fi ; \
- if which dpkg-parsechangelog > /dev/null 2>&1; then \
- echo `dpkg-parsechangelog -S Version`$$ci; \
- else \
- echo `grep ^aptly -m1 debian/changelog | sed 's/.*(\([^)]\+\)).*/\1/'`$$ci ; \
- fi
-
-releasetype: # Print release type (ci/release)
- @reltype=ci ; \
- gitbranch=`git rev-parse --abbrev-ref HEAD` ; \
- if [ "$$gitbranch" = "HEAD" ] && [ "$$FORCE_CI" != "true" ]; then \
- gittag=`git describe --tags --exact-match` ;\
- if echo "$$gittag" | grep -q '^v[0-9]'; then \
- reltype=release ; \
- fi ; \
- fi ; \
- echo $$reltype
+serve: prepare swagger-install ## Run development server (auto recompiling)
+ test -f $(BINPATH)/air || go install github.com/air-verse/air@v1.52.3
+ cp debian/aptly.conf ~/.aptly.conf
+ sed -i /enableSwaggerEndpoint/s/false/true/ ~/.aptly.conf
+ PATH=$(BINPATH):$$PATH air -build.pre_cmd 'swag init -q --markdownFiles docs --generalInfo docs/swagger.conf' -build.exclude_dir docs,system,debian,pgp/keyrings,pgp/test-bins,completion.d,man,deb/testdata,console,_man,cmd,systemd -- api serve -listen 0.0.0.0:3142
-build: ## Build aptly
- # install and initialize swagger
- unset GOBIN; go install github.com/swaggo/swag/cmd/swag@latest
- PATH=$(BINPATH)/:$(PATH) swag init -q
- # prepare
- go mod tidy
- go generate
- # build
- go build -o build/aptly
-
-dev-server: prepare ## Run dev-server
- go install github.com/air-verse/air@v1.52.3
- cp debian/aptly.conf /var/lib/aptly/.aptly.conf
- sed -i /enableSwaggerEndpoint/s/false/true/ /var/lib/aptly/.aptly.conf
- PATH=$(BINPATH):$$PATH air -build.pre_cmd 'swag init -q' -build.exclude_dir system -build.exclude_dir debian -build.exclude_dir docs -- api serve -listen 0.0.0.0:3142
-
-dpkg: ## Build debian packages
+dpkg: prepare swagger ## Build debian packages
@test -n "$(DEBARCH)" || (echo "please define DEBARCH"; exit 1)
- # go generate
- GOPATH=$$PWD/.go go generate -v
- # install and initialize swagger
- go install github.com/swaggo/swag/cmd/swag@latest
- PATH=$(BINPATH)/:$(PATH) swag init -q
# set debian version
@if [ "`make -s releasetype`" = "ci" ]; then \
echo CI Build, setting version... ; \
@@ -150,12 +126,7 @@ dpkg: ## Build debian packages
mkdir -p build && mv ../*.deb build/ ; \
cd build && ls -l *.deb
-binaries: ## Build binary releases (FreeBSD, MacOS, Linux tar)
- # set version
- @make version > VERSION
- # install and initialize swagger
- GOOS=linux GOARCH=amd64 go install github.com/swaggo/swag/cmd/swag@latest
- PATH=$(BINPATH)/:$(PATH) swag init -q
+binaries: prepare swagger ## Build binary releases (FreeBSD, MacOS, Linux tar)
# build aptly
GOOS=$(GOOS) GOARCH=$(GOARCH) go build -o build/tmp/aptly -ldflags='-extldflags=-static'
# install
@@ -178,35 +149,45 @@ docker-image: ## Build aptly-dev docker image
@docker build -f system/Dockerfile . -t aptly-dev
docker-build: ## Build aptly in docker container
- @docker run -it --rm -v ${PWD}:/work/src aptly-dev /work/src/system/run-aptly-cmd make build
+ @docker run -it --rm -v ${PWD}:/work/src aptly-dev /work/src/system/docker-wrapper build
docker-shell: ## Run aptly and other commands in docker container
- @docker run -it --rm -v ${PWD}:/work/src aptly-dev /work/src/system/run-aptly-cmd
+ @docker run -it --rm -v ${PWD}:/work/src aptly-dev /work/src/system/docker-wrapper || true
docker-deb: ## Build debian packages in docker container
- @docker run -it --rm -v ${PWD}:/work/src aptly-dev /work/src/system/build-deb
+ @docker run -it --rm -v ${PWD}:/work/src aptly-dev /work/src/system/docker-wrapper dpkg DEBARCH=amd64
-docker-unit-tests: ## Run unit tests in docker container
- @docker run -it --rm -v ${PWD}:/app aptly-dev /app/system/run-unit-tests
+docker-unit-test: ## Run unit tests in docker container
+ @docker run -it --rm -v ${PWD}:/work/src aptly-dev /work/src/system/docker-wrapper test
-docker-system-tests: ## Run system tests in docker container (add TEST=t04_mirror or TEST=UpdateMirror26Test to run only specific tests)
- @docker run -it --rm -v ${PWD}:/app aptly-dev /app/system/run-system-tests $(TEST)
+docker-system-test: ## Run system tests in docker container (add TEST=t04_mirror or TEST=UpdateMirror26Test to run only specific tests)
+ @docker run -it --rm -v ${PWD}:/work/src aptly-dev /work/src/system/docker-wrapper system-test $(TEST)
-docker-dev-server: ## Run development server (auto recompiling) on http://localhost:3142
- @docker run -it --rm -p 3142:3142 -v ${PWD}:/work/src aptly-dev /work/src/system/docker-wrapper dev-server
+docker-serve: ## Run development server (auto recompiling) on http://localhost:3142
+ @docker run -it --rm -p 3142:3142 -v ${PWD}:/work/src aptly-dev /work/src/system/docker-wrapper serve || true
docker-lint: ## Run golangci-lint in docker container
- @docker run -it --rm -v ${PWD}:/app -e GOLANGCI_LINT_VERSION=$(GOLANGCI_LINT_VERSION) aptly-dev /app/system/run-golangci-lint
+ @docker run -it --rm -v ${PWD}:/work/src aptly-dev /work/src/system/docker-wrapper lint
docker-binaries: ## Build binary releases (FreeBSD, MacOS, Linux tar) in docker container
- @docker run -it --rm -v ${PWD}:/app aptly-dev /app/system/build-binaries
+ @docker run -it --rm -v ${PWD}:/work/src aptly-dev /work/src/system/docker-wrapper binaries
-flake8: ## run flake8 on system tests
- flake8 system
+docker-man: ## Create man page in docker container
+ @docker run -it --rm -v ${PWD}:/work/src aptly-dev /work/src/system/docker-wrapper man
-clean: ## remove local build and module cache
- test -d .go/ && chmod u+w -R .go/ && rm -rf .go/ || true
- rm -rf build/ docs/ obj-*-linux-gnu*
- rm -f unit.out aptly.test
+mem.png: mem.dat mem.gp
+ gnuplot mem.gp
+ open mem.png
-.PHONY: help man prepare version binaries docker-release docker-system-tests docker-unit-tests docker-lint docker-build docker-image build docker-shell clean releasetype dpkg dev-server docker-dev-server flake8
+man: ## Create man pages
+ make -C man
+
+clean: ## remove local build and module cache
+ # Clean all generated and build files
+ find .go/ -type d ! -perm -u=w -exec chmod u+w {} \;
+ rm -rf .go/
+ rm -rf build/ obj-*-linux-gnu* tmp/
+ rm -f unit.out aptly.test VERSION docs/docs.go docs/swagger.json docs/swagger.yaml docs/swagger.conf
+ find system/ -type d -name __pycache__ -exec rm -rf {} \; 2>/dev/null || true
+
+.PHONY: help man prepare swagger version binaries docker-release docker-system-tests docker-unit-tests docker-lint docker-build docker-image build docker-shell clean releasetype dpkg serve docker-serve flake8
diff --git a/api/api.go b/api/api.go
index a52406d80..c66a44b1b 100644
--- a/api/api.go
+++ b/api/api.go
@@ -23,12 +23,38 @@ import (
// 3. SnapshotCollection
// 4. PublishedRepoCollection
-// GET /api/version
+type aptlyVersion struct {
+ // Aptly Version
+ Version string `json:"Version"`
+}
+
+// @Summary Aptly version
+// @Description **Get aptly version**
+// @Description Returns the aptly version
+// @Tags Status
+// @Produce json
+// @Success 200 {object} aptlyVersion
+// @Router /api/version [get]
func apiVersion(c *gin.Context) {
c.JSON(200, gin.H{"Version": aptly.Version})
}
-// GET /api/ready
+type aptlyStatus struct {
+ // Aptly Status
+ Status string `json:"Status" example:"'Aptly is ready', 'Aptly is unavailable', 'Aptly is healthy'"`
+}
+
+// @Summary Ready State
+// @Description **Get aptly ready state**
+// @Description
+// @Description Return aptly ready state:
+// @Description - `Aptly is ready` (HTTP 200)
+// @Description - `Aptly is unavailable` (HTTP 503)
+// @Tags Status
+// @Produce json
+// @Success 200 {object} aptlyStatus "Aptly is ready"
+// @Failure 503 {object} aptlyStatus "Aptly is unavailable"
+// @Router /api/ready [get]
func apiReady(isReady *atomic.Value) func(*gin.Context) {
return func(c *gin.Context) {
if isReady == nil || !isReady.Load().(bool) {
@@ -40,7 +66,15 @@ func apiReady(isReady *atomic.Value) func(*gin.Context) {
}
}
-// GET /api/healthy
+// @Summary Health State
+// @Description **Get aptly health state**
+// @Description
+// @Description Return aptly health state:
+// @Description - `Aptly is healthy` (HTTP 200)
+// @Tags Status
+// @Produce json
+// @Success 200 {object} aptlyStatus
+// @Router /api/healthy [get]
func apiHealthy(c *gin.Context) {
c.JSON(200, gin.H{"Status": "Aptly is healthy"})
}
diff --git a/api/db.go b/api/db.go
index 3f8b826dd..5f7727fff 100644
--- a/api/db.go
+++ b/api/db.go
@@ -11,7 +11,14 @@ import (
"github.com/gin-gonic/gin"
)
-// POST /api/db/cleanup
+// @Summary TODO
+// @Description **ToDo**
+// @Description To Do
+// @Tags Database
+// @Produce json
+// @Success 200 {object} string "msg"
+// @Failure 404 {object} Error "Not Found"
+// @Router /api/db/cleanup [post]
func apiDbCleanup(c *gin.Context) {
resources := []string{string(task.AllResourcesKey)}
diff --git a/api/files.go b/api/files.go
index bd9296cc1..09e081e77 100644
--- a/api/files.go
+++ b/api/files.go
@@ -33,10 +33,10 @@ func verifyDir(c *gin.Context) bool {
return true
}
-// @Summary Get files
-// @Description Get list of uploaded files.
+// @Summary List Files
+// @Description **Get list of uploaded files**
// @Tags Files
-// @Produce json
+// @Produce json
// @Success 200 {array} string "List of files"
// @Router /api/files [get]
func apiFilesListDirs(c *gin.Context) {
@@ -66,7 +66,14 @@ func apiFilesListDirs(c *gin.Context) {
c.JSON(200, list)
}
-// POST /files/:dir/
+// @Summary TODO
+// @Description **ToDo**
+// @Description To Do
+// @Tags Files
+// @Produce json
+// @Success 200 {object} string "msg"
+// @Failure 404 {object} Error "Not Found"
+// @Router /api/files/{dir} [post]
func apiFilesUpload(c *gin.Context) {
if !verifyDir(c) {
return
@@ -120,7 +127,14 @@ func apiFilesUpload(c *gin.Context) {
}
-// GET /files/:dir
+// @Summary TODO
+// @Description **ToDo**
+// @Description To Do
+// @Tags Files
+// @Produce json
+// @Success 200 {object} string "msg"
+// @Failure 404 {object} Error "Not Found"
+// @Router /api/files/{dir} [get]
func apiFilesListFiles(c *gin.Context) {
if !verifyDir(c) {
return
@@ -158,7 +172,14 @@ func apiFilesListFiles(c *gin.Context) {
c.JSON(200, list)
}
-// DELETE /files/:dir
+// @Summary TODO
+// @Description **ToDo**
+// @Description To Do
+// @Tags Files
+// @Produce json
+// @Success 200 {object} string "msg"
+// @Failure 404 {object} Error "Not Found"
+// @Router /api/files/{dir} [delete]
func apiFilesDeleteDir(c *gin.Context) {
if !verifyDir(c) {
return
@@ -173,7 +194,14 @@ func apiFilesDeleteDir(c *gin.Context) {
c.JSON(200, gin.H{})
}
-// DELETE /files/:dir/:name
+// @Summary TODO
+// @Description **ToDo**
+// @Description To Do
+// @Tags Files
+// @Produce json
+// @Success 200 {object} string "msg"
+// @Failure 404 {object} Error "Not Found"
+// @Router /api/files/{dir}/{name} [delete]
func apiFilesDeleteFile(c *gin.Context) {
if !verifyDir(c) {
return
diff --git a/api/gpg.go b/api/gpg.go
index 605b6c1db..7d837bf67 100644
--- a/api/gpg.go
+++ b/api/gpg.go
@@ -11,7 +11,14 @@ import (
"github.com/gin-gonic/gin"
)
-// POST /api/gpg
+// @Summary TODO
+// @Description **ToDo**
+// @Description To Do
+// @Tags GPG
+// @Produce json
+// @Success 200 {object} string "msg"
+// @Failure 404 {object} Error "Not Found"
+// @Router /api/gpg [post]
func apiGPGAddKey(c *gin.Context) {
var b struct {
Keyserver string
diff --git a/api/graph.go b/api/graph.go
index d04e687f8..cd04063b0 100644
--- a/api/graph.go
+++ b/api/graph.go
@@ -12,6 +12,14 @@ import (
"github.com/gin-gonic/gin"
)
+// @Summary TODO
+// @Description **ToDo**
+// @Description To Do
+// @Tags Graph
+// @Produce json
+// @Success 200 {object} string "msg"
+// @Failure 404 {object} Error "Not Found"
+// @Router /api/graph [get]
// GET /api/graph.:ext?layout=[vertical|horizontal(default)]
func apiGraph(c *gin.Context) {
var (
diff --git a/api/packages.go b/api/packages.go
index 09ae785a5..e649a80e4 100644
--- a/api/packages.go
+++ b/api/packages.go
@@ -4,7 +4,14 @@ import (
"github.com/gin-gonic/gin"
)
-// GET /api/packages/:key
+// @Summary TODO
+// @Description **ToDo**
+// @Description To Do
+// @Tags Packages
+// @Produce json
+// @Success 200 {object} string "msg"
+// @Failure 404 {object} Error "Not Found"
+// @Router /api/packages/{key} [get]
func apiPackagesShow(c *gin.Context) {
collectionFactory := context.NewCollectionFactory()
p, err := collectionFactory.PackageCollection().ByKey([]byte(c.Params.ByName("key")))
diff --git a/api/publish.go b/api/publish.go
index a520878a0..9c759ae36 100644
--- a/api/publish.go
+++ b/api/publish.go
@@ -84,7 +84,14 @@ func apiPublishList(c *gin.Context) {
c.JSON(200, result)
}
-// POST /publish/:prefix
+// @Summary TODO
+// @Description **ToDo**
+// @Description To Do
+// @Tags Publish
+// @Produce json
+// @Success 200 {object} string "msg"
+// @Failure 404 {object} Error "Not Found"
+// @Router /api/publish/{prefix} [post]
func apiPublishRepoOrSnapshot(c *gin.Context) {
param := parseEscapedPath(c.Params.ByName("prefix"))
storage, prefix := deb.ParsePrefix(param)
@@ -246,7 +253,14 @@ func apiPublishRepoOrSnapshot(c *gin.Context) {
})
}
-// PUT /publish/:prefix/:distribution
+// @Summary TODO
+// @Description **ToDo**
+// @Description To Do
+// @Tags Publish
+// @Produce json
+// @Success 200 {object} string "msg"
+// @Failure 404 {object} Error "Not Found"
+// @Router /api/publish/{prefix}/{distribution} [put]
func apiPublishUpdateSwitch(c *gin.Context) {
param := parseEscapedPath(c.Params.ByName("prefix"))
storage, prefix := deb.ParsePrefix(param)
@@ -364,7 +378,14 @@ func apiPublishUpdateSwitch(c *gin.Context) {
})
}
-// DELETE /publish/:prefix/:distribution
+// @Summary TODO
+// @Description **ToDo**
+// @Description To Do
+// @Tags Publish
+// @Produce json
+// @Success 200 {object} string "msg"
+// @Failure 404 {object} Error "Not Found"
+// @Router /api/publish/{prefix}/{distribution} [delete]
func apiPublishDrop(c *gin.Context) {
force := c.Request.URL.Query().Get("force") == "1"
skipCleanup := c.Request.URL.Query().Get("SkipCleanup") == "1"
diff --git a/api/repos.go b/api/repos.go
index 81425f999..81798d775 100644
--- a/api/repos.go
+++ b/api/repos.go
@@ -18,7 +18,14 @@ import (
"github.com/gin-gonic/gin"
)
-// GET /repos
+// @Summary TODO
+// @Description **ToDo**
+// @Description To Do
+// @Tags Repos
+// @Produce json
+// @Success 200 {object} string "msg"
+// @Failure 404 {object} Error "Not Found"
+// @Router /api/repos [get]
func reposListInAPIMode(localRepos map[string]utils.FileSystemPublishRoot) gin.HandlerFunc {
return func(c *gin.Context) {
c.Writer.Header().Set("Content-Type", "text/html; charset=utf-8")
@@ -35,7 +42,16 @@ func reposListInAPIMode(localRepos map[string]utils.FileSystemPublishRoot) gin.H
}
}
-// GET /repos/:storage/*pkgPath
+// @Summary TODO
+// @Description **ToDo**
+// @Description To Do
+// @Tags Repos
+// @Param storage path string true "Storage"
+// @Param pkgPath path string true "Package Path" allowReserved=true
+// @Produce json
+// @Success 200 {object} string "msg"
+// @Failure 404 {object} Error "Not Found"
+// @Router /api/{storage}/{pkgPath} [get]
func reposServeInAPIMode(c *gin.Context) {
pkgpath := c.Param("pkgPath")
@@ -51,7 +67,8 @@ func reposServeInAPIMode(c *gin.Context) {
}
// @Summary Get repos
-// @Description Get list of available repos. Each repo is returned as in “show” API.
+// @Description **Get list of available repos**
+// @Description Each repo is returned as in “show” API.
// @Tags Repos
// @Produce json
// @Success 200 {array} deb.LocalRepo
@@ -69,7 +86,7 @@ func apiReposList(c *gin.Context) {
c.JSON(200, result)
}
-// @Summary Create repository
+// @Summary TODO
// @Description Create a local repository.
// @Tags Repos
// @Produce json
@@ -108,7 +125,14 @@ func apiReposCreate(c *gin.Context) {
c.JSON(201, repo)
}
-// PUT /api/repos/:name
+// @Summary TODO
+// @Description **ToDo**
+// @Description To Do
+// @Tags Repos
+// @Produce json
+// @Success 200 {object} string "msg"
+// @Failure 404 {object} Error "Not Found"
+// @Router /api/repos/{name} [put]
func apiReposEdit(c *gin.Context) {
var b struct {
Name *string
@@ -180,7 +204,14 @@ func apiReposShow(c *gin.Context) {
c.JSON(200, repo)
}
-// DELETE /api/repos/:name
+// @Summary TODO
+// @Description **ToDo**
+// @Description To Do
+// @Tags Repos
+// @Produce json
+// @Success 200 {object} string "msg"
+// @Failure 404 {object} Error "Not Found"
+// @Router /api/repos/{name} [delete]
func apiReposDrop(c *gin.Context) {
force := c.Request.URL.Query().Get("force") == "1"
name := c.Params.ByName("name")
@@ -215,7 +246,14 @@ func apiReposDrop(c *gin.Context) {
})
}
-// GET /api/repos/:name/packages
+// @Summary TODO
+// @Description **ToDo**
+// @Description To Do
+// @Tags Repos
+// @Produce json
+// @Success 200 {object} string "msg"
+// @Failure 404 {object} Error "Not Found"
+// @Router /api/repos/{name}/packages [get]
func apiReposPackagesShow(c *gin.Context) {
collectionFactory := context.NewCollectionFactory()
collection := collectionFactory.LocalRepoCollection()
@@ -296,7 +334,14 @@ func apiReposPackagesAddDelete(c *gin.Context, taskNamePrefix string, cb func(li
})
}
-// POST /repos/:name/packages
+// @Summary TODO
+// @Description **ToDo**
+// @Description To Do
+// @Tags Repos
+// @Produce json
+// @Success 200 {object} string "msg"
+// @Failure 404 {object} Error "Not Found"
+// @Router /api/repos/{name}/packages [post]
func apiReposPackagesAdd(c *gin.Context) {
apiReposPackagesAddDelete(c, "Add packages to repo ", func(list *deb.PackageList, p *deb.Package, out aptly.Progress) error {
out.Printf("Adding package %s\n", p.Name)
@@ -304,7 +349,14 @@ func apiReposPackagesAdd(c *gin.Context) {
})
}
-// DELETE /repos/:name/packages
+// @Summary TODO
+// @Description **ToDo**
+// @Description To Do
+// @Tags Repos
+// @Produce json
+// @Success 200 {object} string "msg"
+// @Failure 404 {object} Error "Not Found"
+// @Router /api/repos/{name}/packages [delete]
func apiReposPackagesDelete(c *gin.Context) {
apiReposPackagesAddDelete(c, "Delete packages from repo ", func(list *deb.PackageList, p *deb.Package, out aptly.Progress) error {
out.Printf("Removing package %s\n", p.Name)
@@ -313,7 +365,14 @@ func apiReposPackagesDelete(c *gin.Context) {
})
}
-// POST /repos/:name/file/:dir/:file
+// @Summary TODO
+// @Description **ToDo**
+// @Description To Do
+// @Tags Repos
+// @Produce json
+// @Success 200 {object} string "msg"
+// @Failure 404 {object} Error "Not Found"
+// @Router /api/repos/{name}/file/{dir}/{file} [post]
func apiReposPackageFromFile(c *gin.Context) {
// redirect all work to dir method
apiReposPackageFromDir(c)
@@ -454,7 +513,14 @@ func apiReposPackageFromDir(c *gin.Context) {
})
}
-// POST /repos/:name/copy/:src/:file
+// @Summary TODO
+// @Description **ToDo**
+// @Description To Do
+// @Tags Repos
+// @Produce json
+// @Success 200 {object} string "msg"
+// @Failure 404 {object} Error "Not Found"
+// @Router /api/repos/{name}/copy/{src}/{file} [post]
func apiReposCopyPackage(c *gin.Context) {
dstRepoName := c.Params.ByName("name")
srcRepoName := c.Params.ByName("src")
@@ -598,13 +664,27 @@ func apiReposCopyPackage(c *gin.Context) {
})
}
-// POST /repos/:name/include/:dir/:file
+// @Summary TODO
+// @Description **ToDo**
+// @Description To Do
+// @Tags Repos
+// @Produce json
+// @Success 200 {object} string "msg"
+// @Failure 404 {object} Error "Not Found"
+// @Router /api/repos/{name}/include/{dir}/{file} [post]
func apiReposIncludePackageFromFile(c *gin.Context) {
// redirect all work to dir method
apiReposIncludePackageFromDir(c)
}
-// POST /repos/:name/include/:dir
+// @Summary TODO
+// @Description **ToDo**
+// @Description To Do
+// @Tags Repos
+// @Produce json
+// @Success 200 {object} string "msg"
+// @Failure 404 {object} Error "Not Found"
+// @Router /api/repos/{name}/include/{dir} [post]
func apiReposIncludePackageFromDir(c *gin.Context) {
forceReplace := c.Request.URL.Query().Get("forceReplace") == "1"
noRemoveFiles := c.Request.URL.Query().Get("noRemoveFiles") == "1"
diff --git a/api/router.go b/api/router.go
index d5b6e6123..aaf6e7fba 100644
--- a/api/router.go
+++ b/api/router.go
@@ -12,7 +12,7 @@ import (
"github.com/prometheus/client_golang/prometheus/promhttp"
"github.com/rs/zerolog/log"
- _ "github.com/aptly-dev/aptly/docs" // import docs
+ "github.com/aptly-dev/aptly/docs"
swaggerFiles "github.com/swaggo/files"
ginSwagger "github.com/swaggo/gin-swagger"
)
@@ -27,26 +27,22 @@ func apiMetricsGet() gin.HandlerFunc {
}
func redirectSwagger(c *gin.Context) {
+ if c.Request.URL.Path == "/docs/index.html" {
+ c.Redirect(http.StatusMovedPermanently, "/docs.html")
+ return
+ }
if c.Request.URL.Path == "/docs/" {
- c.Redirect(http.StatusMovedPermanently, "/docs/index.html")
+ c.Redirect(http.StatusMovedPermanently, "/docs.html")
+ return
+ }
+ if c.Request.URL.Path == "/docs" {
+ c.Redirect(http.StatusMovedPermanently, "/docs.html")
return
}
c.Next()
}
// Router returns prebuilt with routes http.Handler
-// @title Aptly API
-// @version 1.0
-// @description Aptly REST API Documentation
-
-// @contact.name Aptly
-// @contact.url http://github.com/aptly-dev/aptly
-// @contact.email support@aptly.info
-
-// @license.name MIT License
-// @license.url http://www.
-
-// @BasePath /api
func Router(c *ctx.AptlyContext) http.Handler {
if aptly.EnableDebug {
gin.SetMode(gin.DebugMode)
@@ -73,6 +69,9 @@ func Router(c *ctx.AptlyContext) http.Handler {
router.Use(gin.Recovery(), gin.ErrorLogger())
if c.Config().EnableSwaggerEndpoint {
+ router.GET("docs.html", func(c *gin.Context) {
+ c.Data(http.StatusOK, "text/html; charset=utf-8", docs.DocsHTML)
+ })
router.Use(redirectSwagger)
url := ginSwagger.URL("/docs/doc.json")
router.GET("/docs/*any", ginSwagger.WrapHandler(swaggerFiles.Handler, url))
diff --git a/api/s3.go b/api/s3.go
index 33ed0c83e..19be7537a 100644
--- a/api/s3.go
+++ b/api/s3.go
@@ -4,8 +4,8 @@ import (
"github.com/gin-gonic/gin"
)
-// @Summary Get S3 buckets
-// @Description Get list of S3 buckets.
+// @Summary S3 buckets
+// @Description **Get list of S3 buckets**
// @Tags S3
// @Produce json
// @Success 200 {array} string "List of S3 buckets"
diff --git a/api/snapshot.go b/api/snapshot.go
index 3e84b1f20..fa0e3c204 100644
--- a/api/snapshot.go
+++ b/api/snapshot.go
@@ -39,7 +39,14 @@ func apiSnapshotsList(c *gin.Context) {
c.JSON(200, result)
}
-// POST /api/mirrors/:name/snapshots/
+// @Summary TODO
+// @Description **ToDo**
+// @Description To Do
+// @Tags Snapshots
+// @Produce json
+// @Success 200 {object} string "msg"
+// @Failure 404 {object} Error "Not Found"
+// @Router /api/mirrors/{name}/snapshots [post]
func apiSnapshotsCreateFromMirror(c *gin.Context) {
var (
err error
@@ -98,7 +105,14 @@ func apiSnapshotsCreateFromMirror(c *gin.Context) {
})
}
-// POST /api/snapshots
+// @Summary TODO
+// @Description **ToDo**
+// @Description To Do
+// @Tags Snapshots
+// @Produce json
+// @Success 200 {object} string "msg"
+// @Failure 404 {object} Error "Not Found"
+// @Router /api/snapshots [post]
func apiSnapshotsCreate(c *gin.Context) {
var (
err error
@@ -172,7 +186,14 @@ func apiSnapshotsCreate(c *gin.Context) {
})
}
-// POST /api/repos/:name/snapshots
+// @Summary TODO
+// @Description **ToDo**
+// @Description To Do
+// @Tags Snapshots
+// @Produce json
+// @Success 200 {object} string "msg"
+// @Failure 404 {object} Error "Not Found"
+// @Router /api/repos/{name}/snapshots [post]
func apiSnapshotsCreateFromRepository(c *gin.Context) {
var (
err error
@@ -226,7 +247,14 @@ func apiSnapshotsCreateFromRepository(c *gin.Context) {
})
}
-// PUT /api/snapshots/:name
+// @Summary TODO
+// @Description **ToDo**
+// @Description To Do
+// @Tags Snapshots
+// @Produce json
+// @Success 200 {object} string "msg"
+// @Failure 404 {object} Error "Not Found"
+// @Router /api/snapshots/{name} [put]
func apiSnapshotsUpdate(c *gin.Context) {
var (
err error
@@ -276,7 +304,14 @@ func apiSnapshotsUpdate(c *gin.Context) {
})
}
-// GET /api/snapshots/:name
+// @Summary TODO
+// @Description **ToDo**
+// @Description To Do
+// @Tags Snapshots
+// @Produce json
+// @Success 200 {object} string "msg"
+// @Failure 404 {object} Error "Not Found"
+// @Router /api/snapshots/{name} [get]
func apiSnapshotsShow(c *gin.Context) {
collectionFactory := context.NewCollectionFactory()
collection := collectionFactory.SnapshotCollection()
@@ -296,7 +331,14 @@ func apiSnapshotsShow(c *gin.Context) {
c.JSON(200, snapshot)
}
-// DELETE /api/snapshots/:name
+// @Summary TODO
+// @Description **ToDo**
+// @Description To Do
+// @Tags Snapshots
+// @Produce json
+// @Success 200 {object} string "msg"
+// @Failure 404 {object} Error "Not Found"
+// @Router /api/snapshots/{name} [delete]
func apiSnapshotsDrop(c *gin.Context) {
name := c.Params.ByName("name")
force := c.Request.URL.Query().Get("force") == "1"
@@ -335,7 +377,14 @@ func apiSnapshotsDrop(c *gin.Context) {
})
}
-// GET /api/snapshots/:name/diff/:withSnapshot
+// @Summary TODO
+// @Description **ToDo**
+// @Description To Do
+// @Tags Snapshots
+// @Produce json
+// @Success 200 {object} string "msg"
+// @Failure 404 {object} Error "Not Found"
+// @Router /api/snapshots/{name}/diff/{withSnapshot} [get]
func apiSnapshotsDiff(c *gin.Context) {
onlyMatching := c.Request.URL.Query().Get("onlyMatching") == "1"
@@ -386,7 +435,14 @@ func apiSnapshotsDiff(c *gin.Context) {
c.JSON(200, result)
}
-// GET /api/snapshots/:name/packages
+// @Summary TODO
+// @Description **ToDo**
+// @Description To Do
+// @Tags Snapshots
+// @Produce json
+// @Success 200 {object} string "msg"
+// @Failure 404 {object} Error "Not Found"
+// @Router /api/snapshots/{name}/packages [get]
func apiSnapshotsSearchPackages(c *gin.Context) {
collectionFactory := context.NewCollectionFactory()
collection := collectionFactory.SnapshotCollection()
diff --git a/api/task.go b/api/task.go
index 189d79319..a33510aa0 100644
--- a/api/task.go
+++ b/api/task.go
@@ -20,21 +20,41 @@ func apiTasksList(c *gin.Context) {
c.JSON(200, list.GetTasks())
}
-// POST /tasks-clear
+// @Summary TODO
+// @Description **ToDo**
+// @Description To Do
+// @Tags Tasks
+// @Produce json
+// @Success 200 {object} string "msg"
+// @Router /api/tasks-clear [post]
func apiTasksClear(c *gin.Context) {
list := context.TaskList()
list.Clear()
c.JSON(200, gin.H{})
}
-// GET /tasks-wait
+// @Summary TODO
+// @Description **ToDo**
+// @Description To Do
+// @Tags Tasks
+// @Produce json
+// @Success 200 {object} string "msg"
+// @Failure 404 {object} Error "Not Found"
+// @Router /api/tasks-wait [get]
func apiTasksWait(c *gin.Context) {
list := context.TaskList()
list.Wait()
c.JSON(200, gin.H{})
}
-// GET /tasks/:id/wait
+// @Summary TODO
+// @Description **ToDo**
+// @Description To Do
+// @Tags Tasks
+// @Produce json
+// @Success 200 {object} string "msg"
+// @Failure 404 {object} Error "Not Found"
+// @Router /api/tasks/{id}/wait [get]
func apiTasksWaitForTaskByID(c *gin.Context) {
list := context.TaskList()
id, err := strconv.ParseInt(c.Params.ByName("id"), 10, 0)
@@ -52,7 +72,14 @@ func apiTasksWaitForTaskByID(c *gin.Context) {
c.JSON(200, task)
}
-// GET /tasks/:id
+// @Summary TODO
+// @Description **ToDo**
+// @Description To Do
+// @Tags Tasks
+// @Produce json
+// @Success 200 {object} string "msg"
+// @Failure 404 {object} Error "Not Found"
+// @Router /api/tasks/{id} [get]
func apiTasksShow(c *gin.Context) {
list := context.TaskList()
id, err := strconv.ParseInt(c.Params.ByName("id"), 10, 0)
@@ -71,7 +98,14 @@ func apiTasksShow(c *gin.Context) {
c.JSON(200, task)
}
-// GET /tasks/:id/output
+// @Summary TODO
+// @Description **ToDo**
+// @Description To Do
+// @Tags Tasks
+// @Produce json
+// @Success 200 {object} string "msg"
+// @Failure 404 {object} Error "Not Found"
+// @Router /api/tasks/{id}/output [get]
func apiTasksOutputShow(c *gin.Context) {
list := context.TaskList()
id, err := strconv.ParseInt(c.Params.ByName("id"), 10, 0)
@@ -90,7 +124,14 @@ func apiTasksOutputShow(c *gin.Context) {
c.JSON(200, output)
}
-// GET /tasks/:id/detail
+// @Summary TODO
+// @Description **ToDo**
+// @Description To Do
+// @Tags Tasks
+// @Produce json
+// @Success 200 {object} string "msg"
+// @Failure 404 {object} Error "Not Found"
+// @Router /api/tasks/{id}/detail [get]
func apiTasksDetailShow(c *gin.Context) {
list := context.TaskList()
id, err := strconv.ParseInt(c.Params.ByName("id"), 10, 0)
@@ -109,7 +150,14 @@ func apiTasksDetailShow(c *gin.Context) {
c.JSON(200, detail)
}
-// GET /tasks/:id/return_value
+// @Summary TODO
+// @Description **ToDo**
+// @Description To Do
+// @Tags Tasks
+// @Produce json
+// @Success 200 {object} string "msg"
+// @Failure 404 {object} Error "Not Found"
+// @Router /api/tasks/{id}/return_value [get]
func apiTasksReturnValueShow(c *gin.Context) {
list := context.TaskList()
id, err := strconv.ParseInt(c.Params.ByName("id"), 10, 0)
@@ -127,7 +175,14 @@ func apiTasksReturnValueShow(c *gin.Context) {
c.JSON(200, output)
}
-// DELETE /tasks/:id
+// @Summary TODO
+// @Description **ToDo**
+// @Description To Do
+// @Tags Tasks
+// @Produce json
+// @Success 200 {object} string "msg"
+// @Failure 404 {object} Error "Not Found"
+// @Router /api/tasks/{id} [delete]
func apiTasksDelete(c *gin.Context) {
list := context.TaskList()
id, err := strconv.ParseInt(c.Params.ByName("id"), 10, 0)
@@ -146,7 +201,16 @@ func apiTasksDelete(c *gin.Context) {
c.JSON(200, delTask)
}
-// POST /tasks-dummy
+// FIXME: used for testing only, remove:
+
+// @Summary TODO
+// @Description **ToDo**
+// @Description To Do
+// @Tags Tasks
+// @Produce json
+// @Success 200 {object} string "msg"
+// @Failure 404 {object} Error "Not Found"
+// @Router /api/tasks-dummy [post]
func apiTasksDummy(c *gin.Context) {
resources := []string{"dummy"}
taskName := "Dummy task"
diff --git a/debian/aptly.conf b/debian/aptly.conf
index d091e4745..4d627fc4b 100644
--- a/debian/aptly.conf
+++ b/debian/aptly.conf
@@ -1,38 +1,248 @@
+// vim: : filetype=json
{
+
+// General
+///////////
+
+ // Aptly storage directory
+ // - downloaded packages (`rootDir`/pool)
+ // - database (`rootDir`/db)
+ // - published repositories (`rootDir`/public)
"rootDir": "~/.aptly",
+
+ // number of attempts to open DB if it's locked by other instance; can be overridden with option `-db-open-attempts`
+ "databaseOpenAttempts": -1,
+
+ //
+ "AsyncAPI": false,
+
+ //
+ "enableMetricsEndpoint": false,
+
+ // Enable API documentation on /docs
+ "enableSwaggerEndpoint": false,
+
+ //
+ "logLevel": "info",
+
+ //
+ "logFormat": "default",
+
+ //
+ "serveInAPIMode": false,
+
+ // OBSOLETE
+ // in aptly up to version 1.0.0, package files were stored in internal package pool
+ // with MD5-dervied path, since 1.1.0 package pool layout was changed;
+ // if option is enabled, aptly stops checking for legacy paths;
+ // by default option is enabled for new aptly installations and disabled when
+ // upgrading from older versions
+ "skipLegacyPool": true,
+
+// Database
+////////////
+
+ //
+ "databaseBackend": {
+ //
+ "type": "",
+ //
+ "url": "",
+ //
+ "dbPath": ""
+ //
+ },
+
+// Mirroring
+/////////////
+
+ // downloader to use
+ // - "default" (normal downloader)
+ // - "grab" (more robust)
+ "downloader": "default",
+
+ // number of parallel download threads to use when downloading packages
"downloadConcurrency": 4,
+
+ // limit in kbytes/sec on download speed while mirroring remote repositories
"downloadSpeedLimit": 0,
+
+ // number of retries for download attempts
"downloadRetries": 0,
- "downloader": "default",
- "databaseOpenAttempts": -1,
+
+ // download source packages per default
+ "downloadSourcePackages": false,
+
+
+ // list of architectures to process; if left empty defaults to all available architectures; can be overridden with option `-architectures`
"architectures": [],
+
+ // follow contents of `Suggests:` field when processing dependencies for the package
"dependencyFollowSuggests": false,
+
+ // follow contents of `Recommends:` field when processing dependencies for the package
"dependencyFollowRecommends": false,
+
+ // when dependency looks like `package-a | package-b`, follow both variants always
"dependencyFollowAllVariants": false,
+
+ // follow dependency from binary package to source package
"dependencyFollowSource": false,
+
+ // print additional details while resolving dependencies (useful for debugging)
"dependencyVerboseResolve": false,
+
+// Signing
+///////////
+
+ // gpg provider to use:
+ // - "internal" (Go internal implementation)
+ // - 'gpg" (external `gpg` utility, uses GnuPG 1.x if available or GnuPG 2.x otherwise)
+ "gpgProvider": "gpg",
+
+ // don't sign published repositories with gpg(1), also can be disabled on per-repo basis using `-skip-signing` flag when publishing
"gpgDisableSign": false,
+
+ // don't verify remote mirrors with gpg(1), also can be disabled on per-mirror basis using `-ignore-signatures` flag when creating and updating mirrors
"gpgDisableVerify": false,
- "gpgProvider": "gpg",
- "downloadSourcePackages": false,
- "skipLegacyPool": true,
+
+
+// PPA
+///////
+
+ // specifies paramaters for short PPA url expansion, if left blank they default to output of `lsb_release` command
"ppaDistributorID": "ubuntu",
+
+ // cwcodename for short PPA url expansion
"ppaCodename": "",
+
+ //
"skipContentsPublishing": false,
+
+ //
"skipBz2Publishing": false,
- "FileSystemPublishEndpoints": {},
- "S3PublishEndpoints": {},
- "SwiftPublishEndpoints": {},
- "AzurePublishEndpoints": {},
- "AsyncAPI": false,
- "enableMetricsEndpoint": false,
- "logLevel": "info",
- "logFormat": "default",
- "serveInAPIMode": false,
- "databaseBackend": {
- "type": "",
- "url": "",
- "dbPath": ""
+
+// Storage Endpoints
+/////////////////////
+
+ // Filesystem publishing endpoints
+ //
+ // aptly defaults to publish to a single publish directory under `rootDir`/public. For
+ // a more advanced publishing strategy, you can define one or more filesystem endpoints in the
+ // `FileSystemPublishEndpoints` list of the aptly configuration file. Each endpoint has a name
+ // and the following associated settings:
+ //
+ // * `rootDir`:
+ // The publish directory, e.g., `/opt/srv/aptly_public`.
+ // * `linkMethod`:
+ // This is one of `hardlink`, `symlink` or `copy`. It specifies how aptly links the
+ // files from the internal pool to the published directory.
+ // If not specified, empty or wrong, this defaults to `hardlink`.
+ // * `verifyMethod`:
+ // This is used only when setting the `linkMethod` to `copy`. Possible values are
+ // `md5` and `size`. It specifies how aptly compares existing links from the
+ // internal pool to the published directory. The `size` method compares only the
+ // file sizes, whereas the `md5` method calculates the md5 checksum of the found
+ // file and compares it to the desired one.
+ // If not specified, empty or wrong, this defaults to `md5`.
+ //
+ // In order to publish to such an endpoint, specify the endpoint as `filesystem:endpoint-name`
+ // with `endpoint-name` as the name given in the aptly configuration file. For example:
+ //
+ // `aptly publish snapshot wheezy-main filesystem:test1:wheezy/daily`
+ "FileSystemPublishEndpoints": {
+ },
+
+ // S3 Endpoint Support
+ //
+ // cloud storage). First, publishing
+ // endpoints should be described in aptly configuration file. Each endpoint has name
+ // and associated settings:
+ //
+ // * `region`:
+ // Amazon region for S3 bucket (e.g. `us-east-1`)
+ // * `bucket`:
+ // bucket name
+ // * `endpoint`:
+ // (optional) when using S3-compatible cloud storage, specify hostname of service endpoint here,
+ // region is ignored if endpoint is set (set region to some human-readable name)
+ // (should be left blank for real Amazon S3)
+ // * `prefix`:
+ // (optional) do publishing under specified prefix in the bucket, defaults to
+ // no prefix (bucket root)
+ // * `acl`:
+ // (optional) assign ACL to published files (one of the canned ACLs in Amazon
+ // terminology). Useful values: `private` (default), `public-read` (public
+ // repository) or `none` (don't set ACL). Public repositories could be consumed by `apt` using
+ // HTTP endpoint (Amazon bucket should be configured for "website hosting"),
+ // for private repositories special apt S3 transport is required.
+ // * `awsAccessKeyID`, `awsSecretAccessKey`:
+ // (optional) Amazon credentials to access S3 bucket. If not supplied,
+ // environment variables `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY`
+ // are used.
+ // * `storageClass`:
+ // (optional) Amazon S3 storage class, defaults to `STANDARD`. Other values
+ // available: `REDUCED_REDUNDANCY` (lower price, lower redundancy)
+ // * `encryptionMethod`:
+ // (optional) server-side encryption method, defaults to none. Currently
+ // the only available encryption method is `AES256`
+ // * `plusWorkaround`:
+ // (optional) workaround misbehavior in apt and Amazon S3
+ // for files with `+` in filename by
+ // creating two copies of package files with `+` in filename: one original
+ // and another one with spaces instead of plus signs
+ // With `plusWorkaround` enabled, package files with plus sign
+ // would be stored twice. aptly might not cleanup files with spaces when published
+ // repository is dropped or updated (switched) to new version of repository (snapshot)
+ // * `disableMultiDel`:
+ // (optional) for S3-compatible cloud storages which do not support `MultiDel` S3 API,
+ // enable this setting (file deletion would be slower with this setting enabled)
+ // * `forceSigV2`:
+ // (optional) disable Signature V4 support, useful with non-AWS S3-compatible object stores
+ // which do not support SigV4, shouldn't be enabled for AWS
+ // * `forceVirtualHostedStyle`:
+ // (optional) disable path style visit, useful with non-AWS S3-compatible object stores
+ // which only support virtual hosted style
+ // * `debug`:
+ // (optional) enables detailed request/response dump for each S3 operation
+ //
+ // In order to publish to S3, specify endpoint as `s3:endpoint-name:` before
+ // publishing prefix on the command line, e.g.:
+ //
+ // `aptly publish snapshot wheezy-main s3:test:`
+ "S3PublishEndpoints": {
+ },
+
+ // Swift Endpoint Support
+ //
+ // aptly could be configured to publish repository directly to OpenStack Swift. First,
+ // publishing endpoints should be described in aptly configuration file. Each endpoint
+ // has name and associated settings:
+ //
+ // * `container`:
+ // container name
+ // * `prefix`:
+ // (optional) do publishing under specified prefix in the container, defaults to
+ // no prefix (container root)
+ // * `osname`, `password`:
+ // (optional) OpenStack credentials to access Keystone. If not supplied,
+ // environment variables `OS_USERNAME` and `OS_PASSWORD` are used.
+ // * `tenant`, `tenantid`:
+ // (optional) OpenStack tenant name and id (in order to use v2 authentication).
+ // * `authurl`:
+ // (optional) the full url of Keystone server (including port, and version).
+ // example `http://identity.example.com:5000/v2.0`
+ //
+ // In order to publish to Swift, specify endpoint as `swift:endpoint-name:` before
+ // publishing prefix on the command line, e.g.:
+ //
+ // `aptly publish snapshot jessie-main swift:test:`
+ "SwiftPublishEndpoints": {
},
- "enableSwaggerEndpoint": false
+
+ // Azure Endpoint Support
+ //
+ "AzurePublishEndpoints": {
+ }
+
}
diff --git a/debian/control b/debian/control
index eeb0b996e..7e9dc5452 100644
--- a/debian/control
+++ b/debian/control
@@ -87,7 +87,7 @@ Testsuite: autopkgtest-pkg-go
Package: aptly
Architecture: any
-Depends: ${misc:Depends}, ${shlibs:Depends}, bzip2, xz-utils, adduser
+Depends: ${misc:Depends}, ${shlibs:Depends}, bzip2, xz-utils, adduser, gpg (>= 2.2.12)
Built-Using: ${misc:Static-Built-Using}, ${misc:Built-Using}
Description: Debian repository management tool
aptly is a Swiss army knife for Debian repository management.
diff --git a/docs/Database.md b/docs/Database.md
new file mode 100644
index 000000000..8786b3ff5
--- /dev/null
+++ b/docs/Database.md
@@ -0,0 +1 @@
+# Aptly Database Operations
diff --git a/docs/Publish.md b/docs/Publish.md
new file mode 100644
index 000000000..d87c87cc7
--- /dev/null
+++ b/docs/Publish.md
@@ -0,0 +1,29 @@
+# Aptly Publish Points
+
+
+Publish snapshot or local repo as Debian repository which could be
+served by HTTP/FTP/rsync server. Repository is signed by user's key with
+GnuPG. Key should be created beforehand (see section GPG Keys below).
+Published repository could be consumed directly by apt.
+
+Repositories could be published to Amazon S3 service: create bucket,
+[configure publishing endpoint](/doc/feature/s3/) and use S3 endpoint when
+publishing.
+
+
+#### GPG Keys
+
+GPG key is required to sign any published repository. Key should be
+generated before publishing first repository.
+
+Key generation, storage, backup and revocation is out of scope of this
+document, there are many tutorials available, e.g. [this one](http://fedoraproject.org/wiki/Creating_GPG_Keys).
+
+Publiс part of the key should be exported from your keyring using `gpg --export --armor` and
+imported into apt keyring using `apt-key` tool on all machines that would be using published
+repositories.
+
+Signing releases is highly recommended, but if you want to skip it, you
+can either use `gpgDisableSign` configuration option or `--skip-signing`
+flag.
+
diff --git a/docs/Status.md b/docs/Status.md
new file mode 100644
index 000000000..e19de2dbc
--- /dev/null
+++ b/docs/Status.md
@@ -0,0 +1,10 @@
+# Aptly Status Information
+